"use client";

import { useState, useRef, useCallback } from "react";
import Link from "next/link";
import { Card, Table, Alert, Badge, Spinner, Button } from "react-bootstrap";
import { Cassette, CloudArrowUp, PersonFill, TagFill, CpuFill, ArrowRight } from "react-bootstrap-icons";
import { computeMd5 } from "@/utils/md5";
import { identifyTape } from "./actions";
import type { TapeMatch } from "@/server/repo";

const SUPPORTED_EXTS = [".tap", ".tzx", ".pzx", ".csw", ".p", ".o"];

type State =
  | { kind: "idle" }
  | { kind: "hashing" }
  | { kind: "identifying" }
  | { kind: "results"; matches: TapeMatch[]; fileName: string }
  | { kind: "not-found"; fileName: string }
  | { kind: "error"; message: string };

function formatBytes(bytes: number): string {
  if (bytes < 1024) return `${bytes} B`;
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
}

export default function TapeIdentifier() {
  const [state, setState] = useState<State>({ kind: "idle" });
  const [dragOver, setDragOver] = useState(false);
  const inputRef = useRef<HTMLInputElement>(null);

  const processFile = useCallback(async (file: File) => {
    const ext = file.name.substring(file.name.lastIndexOf(".")).toLowerCase();
    if (!SUPPORTED_EXTS.includes(ext)) {
      setState({
        kind: "error",
        message: `Unsupported file type "${ext}". Supported: ${SUPPORTED_EXTS.join(", ")}`,
      });
      return;
    }

    setState({ kind: "hashing" });
    try {
      const md5 = await computeMd5(file);
      setState({ kind: "identifying" });
      const matches = await identifyTape(md5, file.size);
      if (matches.length > 0) {
        setState({ kind: "results", matches, fileName: file.name });
      } else {
        setState({ kind: "not-found", fileName: file.name });
      }
    } catch {
      setState({ kind: "error", message: "Something went wrong. Please try again." });
    }
  }, []);

  const handleDrop = useCallback(
    (e: React.DragEvent) => {
      e.preventDefault();
      setDragOver(false);
      const file = e.dataTransfer.files[0];
      if (file) processFile(file);
    },
    [processFile]
  );

  const handleFileInput = useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => {
      const file = e.target.files?.[0];
      if (file) processFile(file);
      // Reset so re-selecting the same file triggers change
      e.target.value = "";
    },
    [processFile]
  );

  const reset = useCallback(() => {
    setState({ kind: "idle" });
  }, []);

  // Dropzone view (idle, hashing, identifying, error)
  if (state.kind === "results" || state.kind === "not-found") {
    return (
      <Card className="border-0 shadow-sm">
        <Card.Body>
          <Card.Title className="d-flex align-items-center gap-2 mb-3">
            <Cassette size={22} aria-hidden />
            Tape Identifier
          </Card.Title>

          {state.kind === "results" ? (
            <>
              <p className="text-secondary mb-3">
                <strong>{state.fileName}</strong> matched {state.matches.length === 1 ? "1 entry" : `${state.matches.length} entries`}:
              </p>
              {state.matches.map((m) => (
                <Card key={m.downloadId} className="border mb-3">
                  <Card.Body>
                    <div className="d-flex justify-content-between align-items-start mb-2">
                      <Card.Title as="h6" className="mb-0">
                        <Link href={`/zxdb/entries/${m.entryId}`} className="text-decoration-none">
                          {m.entryTitle}
                        </Link>
                      </Card.Title>
                      {m.releaseYear && (
                        <Badge bg="secondary" className="ms-2">{m.releaseYear}</Badge>
                      )}
                    </div>

                    {(m.authors.length > 0 || m.genre || m.machinetype) && (
                      <div className="d-flex flex-wrap gap-2 mb-2 small text-secondary">
                        {m.authors.length > 0 && (
                          <span><PersonFill className="me-1" aria-hidden />{m.authors.join(", ")}</span>
                        )}
                        {m.genre && (
                          <span><TagFill className="me-1" aria-hidden />{m.genre}</span>
                        )}
                        {m.machinetype && (
                          <span><CpuFill className="me-1" aria-hidden />{m.machinetype}</span>
                        )}
                      </div>
                    )}

                    <Table size="sm" borderless className="mb-2 small" style={{ maxWidth: 500 }}>
                      <tbody>
                        <tr>
                          <td className="text-secondary ps-0" style={{ width: 90 }}>File</td>
                          <td className="font-monospace">{m.innerPath}</td>
                        </tr>
                        <tr>
                          <td className="text-secondary ps-0">Size</td>
                          <td>{formatBytes(m.sizeBytes)}</td>
                        </tr>
                        <tr>
                          <td className="text-secondary ps-0">MD5</td>
                          <td className="font-monospace">{m.md5}</td>
                        </tr>
                        <tr>
                          <td className="text-secondary ps-0">CRC32</td>
                          <td className="font-monospace">{m.crc32}</td>
                        </tr>
                      </tbody>
                    </Table>

                    <Link
                      href={`/zxdb/entries/${m.entryId}`}
                      className="btn btn-outline-primary btn-sm"
                    >
                      View entry <ArrowRight className="ms-1" aria-hidden />
                    </Link>
                  </Card.Body>
                </Card>
              ))}
            </>
          ) : (
            <p className="text-secondary mb-3">
              No matching tape found in ZXDB for <strong>{state.fileName}</strong>.
            </p>
          )}

          <Button variant="outline-primary" size="sm" onClick={reset}>
            Identify another tape
          </Button>
        </Card.Body>
      </Card>
    );
  }

  const isProcessing = state.kind === "hashing" || state.kind === "identifying";

  return (
    <Card className="border-0 shadow-sm">
      <Card.Body>
        <Card.Title className="d-flex align-items-center gap-2 mb-3">
          <Cassette size={22} aria-hidden />
          Tape Identifier
        </Card.Title>

        <div
          className={`rounded-3 p-4 text-center ${dragOver ? "bg-primary bg-opacity-10 border-primary" : "border-secondary border-opacity-25"}`}
          style={{
            border: "2px dashed",
            cursor: isProcessing ? "wait" : "pointer",
            transition: "background-color 0.15s, border-color 0.15s",
          }}
          onDragOver={(e) => {
            e.preventDefault();
            if (!isProcessing) setDragOver(true);
          }}
          onDragLeave={() => setDragOver(false)}
          onDrop={isProcessing ? (e) => e.preventDefault() : handleDrop}
          onClick={isProcessing ? undefined : () => inputRef.current?.click()}
        >
          {isProcessing ? (
            <div className="py-2">
              <Spinner animation="border" size="sm" variant="primary" className="me-2" />
              <span className="text-secondary">
                {state.kind === "hashing" ? "Computing hash\u2026" : "Searching ZXDB\u2026"}
              </span>
            </div>
          ) : (
            <>
              <div className="mb-2">
                <CloudArrowUp size={32} style={{ opacity: 0.5 }} aria-hidden />
              </div>
              <p className="mb-1 text-secondary">
                Drop a tape file to identify it
              </p>
              <p className="mb-0 small text-secondary">
                {SUPPORTED_EXTS.join(" ")} &mdash; or{" "}
                <span className="text-primary" style={{ textDecoration: "underline", cursor: "pointer" }}>
                  choose file
                </span>
              </p>
            </>
          )}

          <input
            ref={inputRef}
            type="file"
            accept={SUPPORTED_EXTS.join(",")}
            className="d-none"
            onChange={handleFileInput}
          />
        </div>

        {state.kind === "error" && (
          <Alert variant="warning" className="mt-3 mb-0 py-2 small">
            {state.message}
          </Alert>
        )}
      </Card.Body>
    </Card>
  );
}
