"use client";

import { FormEvent, useEffect, useId, useRef, useState } from "react";
import { beginSubmit, canSubmit, type FormStatus } from "@/lib/form-state";
import { isProxyDownloadPath } from "@/lib/proxy-path";
import { validateTikTokUrl } from "@/lib/tiktok-url";
import type { MediaResult } from "@/lib/types";
import { Results } from "@/components/Results";

type RequestError = { code: string; message: string };

function asMedia(value: unknown): MediaResult | null {
  if (!value || typeof value !== "object") return null;
  const media = value as MediaResult;
  if (!Array.isArray(media.downloads)) return null;
  const downloads = media.downloads.filter(
    (item) => item && typeof item.url === "string" && typeof item.label === "string" && isProxyDownloadPath(item.url),
  );
  if (downloads.length === 0) return null;
  const thumbnail = typeof media.thumbnail === "string" && isProxyDownloadPath(media.thumbnail) ? media.thumbnail : undefined;
  return {
    downloads,
    ...(typeof media.title === "string" ? { title: media.title } : {}),
    ...(typeof media.author === "string" ? { author: media.author } : {}),
    ...(thumbnail ? { thumbnail } : {}),
  };
}

export function Downloader() {
  const inputId = useId();
  const errorId = useId();
  const hintId = useId();
  const inputRef = useRef<HTMLInputElement>(null);
  const resultsRef = useRef<HTMLDivElement>(null);
  const inFlight = useRef(false);
  const [value, setValue] = useState("");
  const [status, setStatus] = useState<FormStatus>("idle");
  const [fieldError, setFieldError] = useState<string | null>(null);
  const [requestError, setRequestError] = useState<RequestError | null>(null);
  const [media, setMedia] = useState<MediaResult | null>(null);
  const [clipboardMessage, setClipboardMessage] = useState<string | null>(null);
  const [unconfigured, setUnconfigured] = useState(false);

  useEffect(() => {
    let cancelled = false;
    fetch("/api/download", { headers: { Accept: "application/json" } })
      .then((response) => response.json())
      .then((data: { configured?: boolean }) => {
        if (!cancelled && data?.configured === false) setUnconfigured(true);
      })
      .catch(() => undefined);
    return () => {
      cancelled = true;
    };
  }, []);

  useEffect(() => {
    if (!clipboardMessage) return;
    const timer = window.setTimeout(() => setClipboardMessage(null), 4000);
    return () => window.clearTimeout(timer);
  }, [clipboardMessage]);

  async function paste() {
    try {
      if (!navigator.clipboard?.readText) {
        setClipboardMessage("Clipboard access is not available in this browser. Paste the link with the keyboard.");
        return;
      }
      const text = await Promise.race([
        navigator.clipboard.readText(),
        new Promise<string>((_, reject) => {
          window.setTimeout(() => reject(new Error("clipboard-timeout")), 2000);
        }),
      ]);
      if (!text.trim()) {
        setClipboardMessage("The clipboard is empty.");
        return;
      }
      setValue(text.trim());
      setFieldError(null);
      setClipboardMessage("Link pasted from clipboard.");
    } catch {
      setClipboardMessage("Clipboard access was blocked. Paste the link with the keyboard instead.");
    }
  }

  function clear() {
    setValue("");
    setFieldError(null);
    setClipboardMessage("Link cleared.");
    inputRef.current?.focus();
  }

  async function runRequest() {
    if (!canSubmit(status) || !beginSubmit(inFlight)) return;
    const validation = validateTikTokUrl(value);
    if (!validation.ok) {
      inFlight.current = false;
      setFieldError(validation.message);
      setRequestError(null);
      setStatus("error");
      return;
    }

    setStatus("loading");
    setFieldError(null);
    setRequestError(null);
    setClipboardMessage(null);

    try {
      const response = await fetch("/api/download", {
        method: "POST",
        headers: { Accept: "application/json", "Content-Type": "application/json" },
        body: JSON.stringify({ url: validation.url }),
      });
      let payload: unknown = null;
      try {
        payload = await response.json();
      } catch {
        payload = null;
      }
      const record = payload && typeof payload === "object" ? (payload as { success?: boolean; media?: unknown; error?: RequestError }) : null;
      if (record?.success) {
        const nextMedia = asMedia(record.media);
        if (!nextMedia) {
          setMedia(null);
          setStatus("error");
          setRequestError({ code: "NO_MEDIA", message: "No downloadable files were returned for this link." });
          return;
        }
        setMedia(nextMedia);
        setStatus("success");
        return;
      }
      setMedia(null);
      setStatus("error");
      setRequestError(
        record?.error?.message
          ? record.error
          : { code: "PROVIDER_ERROR", message: "The link could not be processed. Try again." },
      );
    } catch {
      setMedia(null);
      setStatus("error");
      setRequestError({ code: "NETWORK", message: "The request did not finish. Check your connection and try again." });
    } finally {
      inFlight.current = false;
    }
  }

  function onSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault();
    void runRequest();
  }

  useEffect(() => {
    if (status !== "success") return;
    const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
    resultsRef.current?.scrollIntoView({ block: "nearest", behavior: reduce ? "auto" : "smooth" });
  }, [status]);

  const statusText = status === "loading" ? "Processing the TikTok link." : status === "success" ? "Files are ready to download." : "";

  return (
    <div className="downloader">
      <form method="post" action="/api/download" onSubmit={onSubmit} aria-busy={status === "loading"} noValidate>
        <div className="composer">
          <div className="field">
            <label className="sr-only" htmlFor={inputId}>
              TikTok video link
            </label>
            <input
              ref={inputRef}
              id={inputId}
              name="url"
              type="url"
              inputMode="url"
              autoCapitalize="off"
              autoCorrect="off"
              spellCheck={false}
              autoComplete="off"
              placeholder="Paste TikTok video link here"
              value={value}
              disabled={status === "loading"}
              aria-invalid={fieldError ? true : undefined}
              aria-describedby={`${errorId} ${hintId}`}
              onChange={(event) => {
                setValue(event.target.value);
                setFieldError(null);
              }}
              required
            />
            <div className="field-actions">
              {value ? (
                <button type="button" className="ghost-button" onClick={clear} disabled={status === "loading"}>
                  Clear
                </button>
              ) : null}
              <button type="button" className="ghost-button" onClick={() => void paste()} disabled={status === "loading"}>
                Paste
              </button>
            </div>
          </div>
          <button type="submit" className="button button-hero" disabled={status === "loading"}>
            {status === "loading" ? "Processing…" : "Download"}
          </button>
        </div>
        <p id={hintId} className="privacy-note">
          The link is checked on this server. A public lookup returns the file, and Clipwell saves only that file. Clipwell does not ask for your TikTok password.
        </p>
        <p id={errorId} className="field-error" role={fieldError ? "alert" : undefined}>
          {fieldError}
        </p>
        <p className="clipboard-note" role="status">
          {clipboardMessage}
        </p>
        <div className="sr-only" role="status" aria-live="polite">
          {statusText}
        </div>
      </form>

      {unconfigured && status !== "success" ? (
        <p className="notice notice-warn">Downloader service is not configured.</p>
      ) : null}

      {status === "loading" ? (
        <p className="notice notice-loading" role="status">
          <span className="spinner" aria-hidden="true" />
          Looking up this public post. This can take a few seconds.
        </p>
      ) : null}

      {requestError ? (
        <div className={requestError.code === "NOT_CONFIGURED" ? "notice notice-warn" : "notice notice-error"} role="alert">
          <p>{requestError.message}</p>
          <button type="button" className="button button-secondary" onClick={() => void runRequest()}>
            Try again
          </button>
        </div>
      ) : null}

      {status === "success" && media ? (
        <div ref={resultsRef}>
          <Results
            media={media}
            onReset={() => {
              setStatus("idle");
              setMedia(null);
              setRequestError(null);
              inputRef.current?.focus();
            }}
          />
        </div>
      ) : null}
    </div>
  );
}
