import { useEffect, useRef, useState } from "react";
import { Link } from "react-router";
import { t } from "@lingui/core/macro";
import { Trans } from "@lingui/react/macro";

import { API_URL, VALIDATION } from "../config/api.ts";
import type {
  CreateUrlDumpRequest,
  Dump,
  PlaylistMembership,
  RawDump,
  RawPlaylistMembership,
} from "../model.ts";
import {
  deserializeDump,
  deserializePlaylistMembership,
  parseAPIResponse,
} from "../model.ts";
import { useAuth } from "../hooks/useAuth.ts";
import { useWS } from "../hooks/useWS.ts";
import { dumpUrl, normalizeUrl } from "../utils/urls.ts";
import { MAX_FILE_SIZE } from "../config/upload.ts";
import RichContentCard from "./RichContentCard.tsx";
import { MediaPlayer } from "./MediaPlayer.tsx";
import type { RichContent } from "../model.ts";
import { ErrorCard } from "./ErrorCard.tsx";
import { FileDropZone } from "./FileDropZone.tsx";
import { TextEditor } from "./TextEditor.tsx";
import { Modal } from "./Modal.tsx";
import { PlaylistMembershipPanel } from "./PlaylistMembershipPanel.tsx";
import { friendlyFetchError } from "../utils/apiError.ts";

type Mode = "url" | "file";
type Phase = "create" | "playlist";

type SubmitState =
  | { status: "idle" }
  | { status: "submitting" }
  | { status: "error"; error: string };

type UrlPreview =
  | { status: "idle" }
  | { status: "loading" }
  | { status: "done"; richContent: RichContent | null };

function LocalFilePreview({ file }: { file: File }) {
  const [src, setSrc] = useState<string | null>(null);

  useEffect(() => {
    const url = URL.createObjectURL(file);
    // Blob URL lifecycle requires setState in effect — no synchronous alternative.
    // eslint-disable-next-line react-hooks/set-state-in-effect
    setSrc(url);
    return () => {
      URL.revokeObjectURL(url);
    };
  }, [file]);

  if (!src) return null;
  const mime = file.type;
  if (mime.startsWith("image/")) {
    return <img src={src} alt={file.name} className="local-preview-image" />;
  }
  if (mime.startsWith("video/")) {
    return <MediaPlayer key={src} src={src} kind="video" mime={mime} />;
  }
  if (mime.startsWith("audio/")) {
    return <MediaPlayer key={src} src={src} kind="audio" mime={mime} />;
  }
  return null;
}

interface DumpCreateModalProps {
  onClose: () => void;
  initialUrl?: string;
}

export function DumpCreateModal(
  { onClose, initialUrl = "" }: DumpCreateModalProps,
) {
  const { authFetch } = useAuth();
  const { injectDump } = useWS();

  const [phase, setPhase] = useState<Phase>("create");
  const [createdDump, setCreatedDump] = useState<Dump | null>(null);

  // Create phase state
  const [mode, setMode] = useState<Mode>("url");
  const [url, setUrl] = useState(initialUrl);
  const [file, setFile] = useState<File | null>(null);
  const [comment, setComment] = useState("");
  const [isPrivate, setIsPrivate] = useState(false);
  const [submitState, setSubmitState] = useState<SubmitState>({
    status: "idle",
  });
  const [urlPreview, setUrlPreview] = useState<UrlPreview>({ status: "idle" });
  const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);

  // Playlist phase state
  const [memberships, setMemberships] = useState<PlaylistMembership[]>([]);
  const [playlistsLoading, setPlaylistsLoading] = useState(false);

  // Debounced URL preview
  useEffect(() => {
    if (debounceRef.current) clearTimeout(debounceRef.current);

    let trimmed: string;
    try {
      const u = new URL(normalizeUrl(url));
      if (u.protocol !== "http:" && u.protocol !== "https:") throw new Error();
      trimmed = u.toString();
    } catch {
      setUrlPreview({ status: "idle" });
      return;
    }

    setUrlPreview({ status: "loading" });
    debounceRef.current = setTimeout(async () => {
      try {
        const res = await fetch(
          `${API_URL}/api/preview?url=${encodeURIComponent(trimmed)}`,
        );
        const body = await res.json();
        setUrlPreview({
          status: "done",
          richContent: body.success ? body.data : null,
        });
      } catch {
        setUrlPreview({ status: "done", richContent: null });
      }
    }, 600);

    return () => {
      if (debounceRef.current) clearTimeout(debounceRef.current);
    };
  }, [url]);

  // Paste handler
  useEffect(() => {
    const handler = (e: ClipboardEvent) => {
      const pastedFile = e.clipboardData?.files[0];
      if (pastedFile) {
        setMode("file");
        setUrl("");
        setUrlPreview({ status: "idle" });
        setFile(pastedFile);
        setSubmitState({ status: "idle" });
        return;
      }
      const tag = (e.target as HTMLElement).tagName;
      if (tag === "INPUT" || tag === "TEXTAREA") return;
      const text = e.clipboardData?.getData("text") ?? "";
      try {
        const u = new URL(normalizeUrl(text));
        if (u.protocol === "http:" || u.protocol === "https:") {
          setMode("url");
          setFile(null);
          setUrl(u.toString());
          setSubmitState({ status: "idle" });
        }
      } catch { /* not a URL */ }
    };
    globalThis.addEventListener("paste", handler);
    return () => globalThis.removeEventListener("paste", handler);
  }, []);

  const handleSubmit = async (e: React.SubmitEvent<HTMLFormElement>) => {
    e.preventDefault();
    if (comment.length > VALIDATION.DUMP_COMMENT_MAX) return;
    setSubmitState({ status: "submitting" });

    try {
      let res: Response;

      if (mode === "url") {
        const normalizedUrl = normalizeUrl(url);
        if (!normalizedUrl) {
          setSubmitState({ status: "error", error: t`URL is required.` });
          return;
        }
        setUrl(normalizedUrl);
        const body: CreateUrlDumpRequest = {
          url: normalizedUrl,
          comment: comment.trim() || undefined,
          isPrivate,
        };
        res = await authFetch(`${API_URL}/api/dumps`, {
          method: "POST",
          body: JSON.stringify(body),
        });
      } else {
        if (!file) {
          setSubmitState({
            status: "error",
            error: t`Please select a file.`,
          });
          return;
        }
        if (file.size > MAX_FILE_SIZE) {
          setSubmitState({
            status: "error",
            error: t`File too large (max 50 MB).`,
          });
          return;
        }
        const formData = new FormData();
        formData.append("file", file);
        if (comment.trim()) formData.append("comment", comment.trim());
        formData.append("isPrivate", String(isPrivate));
        res = await authFetch(`${API_URL}/api/dumps`, {
          method: "POST",
          body: formData,
        });
      }

      const apiResponse = parseAPIResponse<RawDump>(await res.json());
      if (apiResponse.success) {
        const dump = deserializeDump(apiResponse.data);
        injectDump(dump);
        setCreatedDump(dump);
        setPhase("playlist");
        setPlaylistsLoading(true);
        authFetch(`${API_URL}/api/playlists/by-dump/${dump.id}/memberships`)
          .then((r) => r.json())
          .then((body) => {
            if (body.success) {
              setMemberships(
                (body.data as RawPlaylistMembership[]).map(
                  deserializePlaylistMembership,
                ),
              );
            }
          })
          .catch(() => {})
          .finally(() => setPlaylistsLoading(false));
      } else {
        setSubmitState({
          status: "error",
          error: apiResponse.error.message,
        });
      }
    } catch (err) {
      setSubmitState({ status: "error", error: friendlyFetchError(err) });
    }
  };

  const toggleMembership = async (membership: PlaylistMembership) => {
    if (!createdDump) return;
    const { playlist, hasDump } = membership;
    if (hasDump) {
      await authFetch(
        `${API_URL}/api/playlists/${playlist.id}/dumps/${createdDump.id}`,
        { method: "DELETE" },
      );
      setMemberships((prev) =>
        prev.map((m) =>
          m.playlist.id === playlist.id ? { ...m, hasDump: false } : m
        )
      );
    } else {
      await authFetch(
        `${API_URL}/api/playlists/${playlist.id}/dumps/${createdDump.id}`,
        { method: "POST" },
      );
      setMemberships((prev) =>
        prev.map((m) =>
          m.playlist.id === playlist.id ? { ...m, hasDump: true } : m
        )
      );
    }
  };

  const submitting = submitState.status === "submitting";

  return (
    <Modal
      title={phase === "create" ? t`New dump` : t`Add to playlist`}
      onClose={onClose}
      wide
    >
      {phase === "create"
        ? (
          <>
            <div className="visibility-toggle">
              <button
                type="button"
                className={mode === "url" ? "active" : ""}
                onClick={() => {
                  setMode("url");
                  setFile(null);
                  setSubmitState({ status: "idle" });
                }}
                disabled={submitting}
              >
                🔗 URL
              </button>
              <button
                type="button"
                className={mode === "file" ? "active" : ""}
                onClick={() => {
                  setMode("file");
                  setUrl("");
                  setUrlPreview({ status: "idle" });
                  setSubmitState({ status: "idle" });
                }}
                disabled={submitting}
              >
                📎 <Trans>File</Trans>
              </button>
            </div>

            <form onSubmit={handleSubmit} className="dump-form">
              {submitState.status === "error" && (
                <ErrorCard
                  title={t`Failed to post`}
                  message={submitState.error}
                />
              )}

              {mode === "url"
                ? (
                  <>
                    <div className="form-group">
                      <label htmlFor="dc-url">
                        <Trans>URL</Trans>
                      </label>
                      <input
                        id="dc-url"
                        type="url"
                        value={url}
                        onChange={(e) => setUrl(e.target.value)}
                        onBlur={(e) => setUrl(normalizeUrl(e.target.value))}
                        onPaste={(e) => {
                          const pastedFile = e.clipboardData.files[0];
                          if (pastedFile) {
                            e.preventDefault();
                            setMode("file");
                            setUrl("");
                            setUrlPreview({ status: "idle" });
                            setFile(pastedFile);
                            setSubmitState({ status: "idle" });
                          }
                        }}
                        disabled={submitting}
                        placeholder="https://..."
                        required
                        autoFocus
                      />
                    </div>
                    {urlPreview.status === "loading" && (
                      <p className="preview-loading">
                        <Trans>Fetching preview…</Trans>
                      </p>
                    )}
                    {urlPreview.status === "done" &&
                      urlPreview.richContent && (
                      <RichContentCard
                        richContent={urlPreview.richContent}
                      />
                    )}
                  </>
                )
                : (
                  <>
                    <FileDropZone
                      file={file}
                      onChange={setFile}
                      disabled={submitting}
                    />
                    {file && <LocalFilePreview file={file} />}
                  </>
                )}

              <div className="form-group">
                <label htmlFor="dc-comment">
                  <Trans>Why are you dumping this?</Trans>
                </label>
                <TextEditor
                  id="dc-comment"
                  value={comment}
                  onChange={setComment}
                  disabled={submitting}
                  placeholder={t`Tell the community what makes this worth their time...`}
                  rows={3}
                  maxLength={VALIDATION.DUMP_COMMENT_MAX}
                />
              </div>

              <div className="visibility-toggle">
                <button
                  type="button"
                  className={!isPrivate ? "active" : ""}
                  disabled={submitting}
                  onClick={() => setIsPrivate(false)}
                >
                  <Trans>Public</Trans>
                </button>
                <button
                  type="button"
                  className={isPrivate ? "active" : ""}
                  disabled={submitting}
                  onClick={() => setIsPrivate(true)}
                >
                  <Trans>Private</Trans>
                </button>
              </div>

              <div className="form-actions">
                <div className="form-actions-right">
                  <button
                    type="button"
                    className="form-cancel"
                    onClick={onClose}
                  >
                    <Trans>Cancel</Trans>
                  </button>
                  <button
                    type="submit"
                    className="btn-primary"
                    disabled={submitting ||
                      comment.length > VALIDATION.DUMP_COMMENT_MAX}
                  >
                    {submitting
                      ? (mode === "url"
                        ? <Trans>Fetching…</Trans>
                        : <Trans>Uploading…</Trans>)
                      : <Trans>Dump it</Trans>}
                  </button>
                </div>
              </div>
            </form>
          </>
        )
        : (
          <>
            {createdDump && (
              <p className="dump-create-success">
                <Trans>Dumped!</Trans>{" "}
                <Link to={dumpUrl(createdDump)} onClick={onClose}>
                  <Trans>View dump →</Trans>
                </Link>
              </p>
            )}

            <PlaylistMembershipPanel
              dumpId={createdDump?.id ?? ""}
              memberships={memberships}
              loading={playlistsLoading}
              onToggle={toggleMembership}
              onPlaylistCreated={(membership) =>
                setMemberships((prev) => [membership, ...prev])}
            />

            <div className="form-actions">
              <div className="form-actions-right">
                <button
                  type="button"
                  className="btn-primary"
                  onClick={onClose}
                >
                  <Trans>Done</Trans>
                </button>
              </div>
            </div>
          </>
        )}
    </Modal>
  );
}
