/**
 * UploadDropzone — drag-drop + file-picker for uploading documents into the
 * active session (PDF, HTML, Markdown).
 */

import { useCallback, useRef, useState } from "react";

import {
  ingestHtmlFromFile,
  ingestMarkdownFromFile,
  ingestPdfFromFile,
} from "@source/index";
import {
  useActiveDocumentId,
  useEngine,
  usePdfByteStore,
} from "./EngineContext";

type UploadFormat = "pdf" | "html" | "markdown" | "unsupported";

interface UploadEntry {
  readonly file: File;
  status: "queued" | "uploading" | "done" | "error";
  error?: string;
}

export interface UploadDropzoneProps {
  /** Optional callback fired after each successful upload. */
  readonly onUploaded?: (documentId: import("@shared/ids").DocumentId) => void;
}

function detectFormat(file: File): UploadFormat {
  const name = file.name.toLowerCase();
  if (file.type === "application/pdf" || name.endsWith(".pdf")) return "pdf";
  if (file.type === "text/html" || name.endsWith(".html") || name.endsWith(".htm")) {
    return "html";
  }
  if (
    file.type === "text/markdown"
    || name.endsWith(".md")
    || name.endsWith(".markdown")
  ) {
    return "markdown";
  }
  return "unsupported";
}

export function UploadDropzone({ onUploaded }: UploadDropzoneProps) {
  const engine = useEngine();
  const byteStore = usePdfByteStore();
  const { setId } = useActiveDocumentId();
  const [entries, setEntries] = useState<readonly UploadEntry[]>([]);
  const [isOver, setIsOver] = useState(false);
  const fileInputRef = useRef<HTMLInputElement | null>(null);

  const processFiles = useCallback(
    async (files: readonly File[]) => {
      if (files.length === 0) return;
      const initial: UploadEntry[] = files.map((file) => {
        const format = detectFormat(file);
        if (format === "unsupported") {
          return {
            file,
            status: "error",
            error: "Unsupported format (PDF, HTML, or Markdown only)",
          };
        }
        return { file, status: "queued" };
      });
      setEntries((prev) => [...prev, ...initial]);

      let lastDocumentId: import("@shared/ids").DocumentId | null = null;
      for (const entry of initial) {
        if (entry.status === "error") continue;
        entry.status = "uploading";
        setEntries((prev) => [...prev]);
        try {
          const format = detectFormat(entry.file);
          const ingested = await (format === "html"
            ? ingestHtmlFromFile(entry.file, byteStore)
            : format === "markdown"
              ? ingestMarkdownFromFile(entry.file, byteStore)
              : ingestPdfFromFile(entry.file, byteStore));
          engine.documents.register(ingested);
          entry.status = "done";
          lastDocumentId = ingested.document.id;
          onUploaded?.(ingested.document.id);
        } catch (err) {
          entry.status = "error";
          entry.error = err instanceof Error ? err.message : String(err);
        }
        setEntries((prev) => [...prev]);
      }
      if (lastDocumentId) setId(lastDocumentId);
    },
    [byteStore, engine, onUploaded, setId],
  );

  const onDrop = useCallback(
    (e: React.DragEvent<HTMLDivElement>) => {
      e.preventDefault();
      setIsOver(false);
      const files = Array.from(e.dataTransfer.files);
      void processFiles(files);
    },
    [processFiles],
  );

  const onDragOver = useCallback((e: React.DragEvent<HTMLDivElement>) => {
    e.preventDefault();
    setIsOver(true);
  }, []);

  const onDragLeave = useCallback(() => {
    setIsOver(false);
  }, []);

  const openPicker = useCallback(() => {
    fileInputRef.current?.click();
  }, []);

  const onPicked = useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => {
      const files = e.target.files ? Array.from(e.target.files) : [];
      void processFiles(files);
      e.target.value = "";
    },
    [processFiles],
  );

  return (
    <div data-testid="upload-dropzone">
      <div
        onDrop={onDrop}
        onDragOver={onDragOver}
        onDragLeave={onDragLeave}
        role="region"
        aria-label="Document upload"
        style={{
          border: `2px dashed ${isOver ? "#0050b3" : "#bbb"}`,
          background: isOver ? "#e8f0ff" : "#fafafa",
          padding: 16,
          textAlign: "center",
          fontSize: 12,
          color: "#555",
          borderRadius: 4,
        }}
      >
        <div>Drop PDF, HTML, or Markdown files here</div>
        <div style={{ margin: "6px 0", color: "#888" }}>or</div>
        <button
          type="button"
          onClick={openPicker}
          data-testid="upload-pick-button"
          style={{
            fontSize: 12,
            padding: "4px 10px",
            border: "1px solid #888",
            background: "white",
            cursor: "pointer",
          }}
        >
          Choose file…
        </button>
        <input
          ref={fileInputRef}
          type="file"
          accept="application/pdf,.pdf,text/html,.html,.htm,text/markdown,.md,.markdown"
          multiple
          onChange={onPicked}
          style={{ display: "none" }}
          data-testid="upload-file-input"
        />
      </div>
      {entries.length > 0 && (
        <ul
          data-testid="upload-progress"
          style={{ listStyle: "none", padding: 0, margin: "8px 0 0", fontSize: 11 }}
        >
          {entries.map((entry, i) => (
            <li
              key={`${entry.file.name}-${i}`}
              data-status={entry.status}
              style={{
                padding: "2px 4px",
                color:
                  entry.status === "error"
                    ? "#7a0000"
                    : entry.status === "done"
                      ? "#0a5a0a"
                      : "#333",
              }}
            >
              {entry.file.name} — {entry.status}
              {entry.error ? `: ${entry.error}` : ""}
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}