import { useCallback, useEffect, useEffectEvent, useRef, useState } from "react";
import { useAuthStore } from "@/stores/auth-store";
import { useCreateParticle, useCreateStreamParticle } from "@/hooks/use-create-particle";
import { useRecorder } from "@/features/compose/use-recorder";
import { particlePath } from "@/lib/particle-path";
import type { ParticlePath } from "@/lib/particle-path";
import { RecordingOverlay } from "@/features/compose/recording-overlay";
import { TextComposeStep } from "@/features/compose/text-compose-step";
import { ConfigureStreamStep } from "@/features/compose/configure-stream-step";
import { apiClient } from "@/api/client";
import { useMediaSettingsStore } from "@/stores/media-settings-store";

type ComposeStep = "idle" | "recording" | "reviewing" | "typing" | "configuring" | "submitting";

interface ComposeOverlayProps {
  networkId: string;
  // Optional target path for reply mode. If not provided, compose creates a new stream.
  targetPath?: ParticlePath;
  onActiveChange?: (active: boolean) => void;
}


const HOLD_THRESHOLD_MS = 250;

/**
 * Self-contained compose overlay. Each consumer renders its own instance
 * with props that determine the mode (new stream vs. reply).
 */
export function ComposeOverlay({
  networkId,
  targetPath,
  onActiveChange,
}: ComposeOverlayProps) {
  const [step, setStep] = useState<ComposeStep>("idle");
  const [error, setError] = useState<string | null>(null);
  const [textContent, setTextContent] = useState("");
  const [mediaStream, setMediaStream] = useState<MediaStream | null>(null);
  const [reviewBlob, setReviewBlob] = useState<Blob | null>(null);
  const [reviewDurationMs, setReviewDurationMs] = useState(0);
  const [reviewMimeType, setReviewMimeType] = useState<string | null>(null);

  const recordingMode = useMediaSettingsStore((s) => s.recordingMode);
  const userEmail = useAuthStore((s) => s.user?.email);
  const createParticle = useCreateParticle();
  const createStream = useCreateStreamParticle();

  // Refs for synchronous reads in keyboard handlers
  const stepRef = useRef(step);
  const recordStartRef = useRef(0);

  const setStepSync = useCallback((next: ComposeStep) => {
    stepRef.current = next;
    setStep(next);
  }, []);

  // Notify parent when active state changes
  useEffect(() => {
    onActiveChange?.(step !== "idle");
  }, [step, onActiveChange]);

  const cancel = useCallback(() => {
    setStepSync("idle");
    setError(null);
    setTextContent("");
    setMediaStream(null);
    setReviewBlob(null);
    setReviewDurationMs(0);
    setReviewMimeType(null);
  }, [setStepSync]);

  const { startRecording, stopRecording, cancelRecording } = useRecorder({
    mode: recordingMode,
    onStreamReady: (stream) => setMediaStream(stream),
    onStreamCleanup: () => setMediaStream(null),
    onFinish: (blob, durationMs, mimeType) => {
      setStepSync("reviewing");
      setReviewBlob(blob);
      setReviewDurationMs(durationMs);
      setReviewMimeType(mimeType);
    },
    onError: (message) => setError(message),
  });

  // --- Submission ---

  const uploadMedia = useCallback(
    async (blob: Blob, mimeType: string) => {
      const ext = "webm";
      const fileName = `recording-${Date.now()}.${ext}`;

      const { object_id, upload_url, upload_headers } =
        await apiClient.prepareUpload({
          network_id: networkId,
          name: fileName,
          content_type: mimeType,
          content_length: blob.size,
        });

      await fetch(upload_url, {
        method: "PUT",
        headers: upload_headers,
        body: blob,
      });

      await apiClient.confirmUpload(object_id);

      return { object_id, size_bytes: blob.size };
    },
    [networkId],
  );

  const createChildParticle = useCallback(
    async (path: ParticlePath) => {
      if (!userEmail) return;

      if (textContent.trim()) {
        await createParticle.mutateAsync({
          path,
          type: "text",
          properties: { content: textContent },
          createdByEmail: userEmail,
        });
      } else if (reviewBlob && reviewMimeType) {
        const { object_id, size_bytes } = await uploadMedia(
          reviewBlob,
          reviewMimeType,
        );

        await createParticle.mutateAsync({
          path,
          type: "media",
          properties: {
            object_id,
            mime_type: reviewMimeType,
            duration_ms: reviewDurationMs,
            size_bytes,
          },
          createdByEmail: userEmail,
        });
      }
    },
    [
      userEmail,
      textContent,
      reviewBlob,
      reviewMimeType,
      reviewDurationMs,
      createParticle,
      uploadMedia,
    ],
  );

  // Reply mode: create particle directly under targetPath
  const onSubmitReply = useEffectEvent(async () => {
    if (!targetPath || !userEmail || stepRef.current === "submitting") return;
    setStepSync("submitting");
    await createChildParticle(targetPath);
    cancel();
  });

  // New stream mode: create stream + first child
  const handleStreamSubmit = useCallback(
    async (streamName: string, visibleTo: string[]) => {
      if (!userEmail || stepRef.current === "submitting") return;
      setStepSync("submitting");

      const streamId = await createStream.mutateAsync({
        networkId,
        properties: {
          name: streamName,
          status: "open",
        },
        createdByEmail: userEmail,
        visibleTo,
      });

      const streamChildrenPath = particlePath(networkId, [streamId]);
      await createChildParticle(streamChildrenPath);

      cancel();
    },
    [networkId, userEmail, createParticle, createChildParticle, cancel],
  );

  // --- Keyboard handling ---

  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      const currentStep = stepRef.current;

      if (currentStep === "typing" || currentStep === "configuring") {
        if (e.key === "Escape") {
          e.preventDefault();
          cancel();
        }
        return;
      }

      const target = e.target as HTMLElement;
      if (
        target.tagName === "INPUT" ||
        target.tagName === "TEXTAREA" ||
        target.isContentEditable
      ) {
        return;
      }

      switch (currentStep) {
        case "idle": {
          if (e.key === "`" && !e.repeat) {
            e.preventDefault();
            recordStartRef.current = Date.now();
            setStepSync("recording");
            startRecording();
          } else if (e.key === "t" || e.key === "T") {
            e.preventDefault();
            setStepSync("typing");
          }
          break;
        }

        case "recording": {
          if (e.key === "`" && !e.repeat) {
            // Second tap stops recording (toggle mode)
            e.preventDefault();
            stopRecording();
          } else if (e.key === "q" || e.key === "Q" || e.key === "Escape") {
            e.preventDefault();
            cancelRecording();
            cancel();
          }
          break;
        }

        case "reviewing": {
          if (e.key === "q" || e.key === "Q" || e.key === "Escape") {
            e.preventDefault();
            cancelRecording();
            cancel();
          } else if (e.key === "Enter") {
            e.preventDefault();
            if (targetPath) {
              onSubmitReply();
            } else {
              setStepSync("configuring");
            }
          }
          break;
        }
      }
    };

    const handleKeyUp = (e: KeyboardEvent) => {
      if (stepRef.current === "recording" && e.key === "`") {
        e.preventDefault();
        // Only stop on release if held long enough (hold-to-record mode).
        // Quick taps are handled by the second keydown (toggle mode).
        if (Date.now() - recordStartRef.current >= HOLD_THRESHOLD_MS) {
          stopRecording();
        }
      }
    };

    window.addEventListener("keydown", handleKeyDown);
    window.addEventListener("keyup", handleKeyUp);
    return () => {
      window.removeEventListener("keydown", handleKeyDown);
      window.removeEventListener("keyup", handleKeyUp);
    };
  }, [targetPath, startRecording, stopRecording, cancelRecording, cancel, setStepSync]);

  // --- Render ---

  if (step === "idle") return null;

  const handleTextAdvance = targetPath
    ? onSubmitReply
    : () => setStepSync("configuring");

  return (
    <>
      {(step === "recording" || step === "reviewing") && (
        <RecordingOverlay
          step={step}
          mediaStream={mediaStream}
          recordingMode={recordingMode}
          reviewBlob={reviewBlob}
          error={error}
          onClose={cancel}
        />
      )}
      {step === "typing" && (
        <TextComposeStep
          textContent={textContent}
          onTextChange={setTextContent}
          onAdvance={handleTextAdvance}
          onCancel={cancel}
        />
      )}
      {!targetPath && step === "configuring" && (
        <ConfigureStreamStep
          networkId={networkId}
          onCancel={cancel}
          onSubmit={handleStreamSubmit}
        />
      )}
      {step === "submitting" && (
        <div className="absolute inset-0 z-50 flex flex-col items-center justify-center bg-black/90">
          <span className="animate-pulse text-sm text-white/60">Sending...</span>
        </div>
      )}
    </>
  );
}
