import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { Button } from "../../components/ui/Button";
import { Dropzone } from "../../components/ui/Dropzone";

type PreviewBackground = "light" | "dark" | "grid";

interface SvgMetadata {
  width?: string;
  height?: string;
  viewBox?: string;
  title?: string;
  elementCount: number;
}

const SAMPLE_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="320" height="220" viewBox="0 0 320 220">
  <defs>
    <linearGradient id="paint" x1="0" y1="0" x2="1" y2="1">
      <stop offset="0" stop-color="#A8FF60"/>
      <stop offset="1" stop-color="#448AFF"/>
    </linearGradient>
  </defs>
  <rect width="320" height="220" rx="28" fill="#161922"/>
  <path d="M70 154 118 66l43 69 30-46 59 65" fill="none" stroke="url(#paint)" stroke-width="15" stroke-linecap="round" stroke-linejoin="round"/>
  <circle cx="231" cy="60" r="18" fill="#FFD740"/>
</svg>`;

function parseSvg(source: string): { metadata?: SvgMetadata; error?: string } {
  const parser = new DOMParser();
  const document = parser.parseFromString(source, "image/svg+xml");
  const parserError = document.querySelector("parsererror");
  if (parserError) {
    return { error: "The SVG contains invalid XML." };
  }

  const root = document.documentElement;
  if (root.localName.toLowerCase() !== "svg") {
    return { error: "The document must have an <svg> root element." };
  }

  return {
    metadata: {
      width: root.getAttribute("width") ?? undefined,
      height: root.getAttribute("height") ?? undefined,
      viewBox: root.getAttribute("viewBox") ?? undefined,
      title: root.querySelector("title")?.textContent?.trim() || undefined,
      elementCount: root.querySelectorAll("*").length,
    },
  };
}

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`;
}

function downloadSvg(source: string, fileName: string) {
  const url = URL.createObjectURL(
    new Blob([source], { type: "image/svg+xml;charset=utf-8" })
  );
  const anchor = document.createElement("a");
  anchor.href = url;
  anchor.download = fileName;
  anchor.click();
  URL.revokeObjectURL(url);
}

export default function SvgViewerUi() {
  const previewRef = useRef<HTMLDivElement | null>(null);
  const [draft, setDraft] = useState(SAMPLE_SVG);
  const [source, setSource] = useState(SAMPLE_SVG);
  const [fileName, setFileName] = useState("plimi-sample.svg");
  const [error, setError] = useState<string>();
  const [zoom, setZoom] = useState(1);
  const [fit, setFit] = useState(true);
  const [background, setBackground] = useState<PreviewBackground>("grid");
  const [showSource, setShowSource] = useState(false);
  const [copied, setCopied] = useState(false);
  const [naturalSize, setNaturalSize] = useState({ width: 320, height: 220 });
  const [previewSize, setPreviewSize] = useState({ width: 0, height: 0 });

  const parsed = useMemo(() => parseSvg(source), [source]);
  const metadata = parsed.metadata;
  const sourceSize = useMemo(() => new Blob([source]).size, [source]);
  const previewUrl = useMemo(
    () =>
      URL.createObjectURL(
        new Blob([source], { type: "image/svg+xml;charset=utf-8" })
      ),
    [source]
  );

  useEffect(() => () => URL.revokeObjectURL(previewUrl), [previewUrl]);

  useEffect(() => {
    const element = previewRef.current;
    if (!element) return;
    const observer = new ResizeObserver(([entry]) => {
      setPreviewSize({
        width: entry.contentRect.width,
        height: entry.contentRect.height,
      });
    });
    observer.observe(element);
    return () => observer.disconnect();
  }, []);

  const applySource = useCallback(
    (nextSource = draft, nextName = fileName) => {
      const result = parseSvg(nextSource);
      if (result.error) {
        setError(result.error);
        return;
      }
      setError(undefined);
      setDraft(nextSource);
      setSource(nextSource);
      setFileName(nextName.toLowerCase().endsWith(".svg") ? nextName : `${nextName}.svg`);
      setFit(true);
      setZoom(1);
    },
    [draft, fileName]
  );

  const loadFile = useCallback(
    async (file: File) => {
      try {
        const text = await file.text();
        applySource(text, file.name);
      } catch {
        setError(`Could not read ${file.name}.`);
      }
    },
    [applySource]
  );

  const fitScale = Math.min(
    1,
    previewSize.width > 48 ? (previewSize.width - 48) / naturalSize.width : 1,
    previewSize.height > 48 ? (previewSize.height - 48) / naturalSize.height : 1
  );
  const displayScale = fit ? fitScale : zoom;

  const backgroundStyle =
    background === "grid"
      ? {
          backgroundColor: "#ffffff",
          backgroundImage:
            "linear-gradient(45deg, #e5e7eb 25%, transparent 25%), linear-gradient(-45deg, #e5e7eb 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #e5e7eb 75%), linear-gradient(-45deg, transparent 75%, #e5e7eb 75%)",
          backgroundPosition: "0 0, 0 8px, 8px -8px, -8px 0",
          backgroundSize: "16px 16px",
        }
      : { backgroundColor: background === "dark" ? "#111318" : "#ffffff" };

  return (
    <div className="flex min-h-[540px] flex-col bg-[var(--p-surface)]">
      <div className="flex flex-col gap-3 border-b border-[var(--p-border)] p-3 md:flex-row md:items-center md:p-4">
        <Dropzone
          accept="image/svg+xml,.svg"
          multiple={false}
          maxSizeMb={10}
          onFilesDrop={(files) => {
            if (files[0]) void loadFile(files[0]);
          }}
          className="min-h-0 w-full p-3 md:w-[280px]"
        />

        <div className="flex flex-wrap items-center gap-2 md:ml-auto">
          <Button
            variant="secondary"
            onClick={() => {
              setFit(false);
              setZoom((value) => Math.max(0.1, value - 0.25));
            }}
          >
            -
          </Button>
          <button
            className="min-w-[72px] rounded-[10px] border border-[var(--p-border)] bg-[var(--p-chip)] px-3 py-2 font-mono text-[12px] text-[var(--p-text)]"
            onClick={() => setFit(true)}
          >
            {fit ? "Fit" : `${Math.round(zoom * 100)}%`}
          </button>
          <Button
            variant="secondary"
            onClick={() => {
              setFit(false);
              setZoom((value) => Math.min(4, value + 0.25));
            }}
          >
            +
          </Button>
          <Button
            variant="secondary"
            onClick={() => {
              setFit(false);
              setZoom(1);
            }}
          >
            100%
          </Button>
          {(["grid", "light", "dark"] as PreviewBackground[]).map((value) => (
            <button
              key={value}
              onClick={() => setBackground(value)}
              className={`rounded-[10px] border px-3 py-2 font-sans text-[12px] capitalize ${
                background === value
                  ? "border-[var(--p-accent)] bg-[var(--p-accent)] text-[var(--p-accent-ink)]"
                  : "border-[var(--p-border)] bg-[var(--p-chip)] text-[var(--p-text)]"
              }`}
            >
              {value}
            </button>
          ))}
        </div>
        {error && !showSource && (
          <div className="w-full font-sans text-[12px] text-red-600 md:basis-full">
            {error}
          </div>
        )}
      </div>

      <div className="flex min-h-0 flex-1 flex-col md:grid md:grid-cols-[minmax(0,1fr)_260px]">
        <div
          ref={previewRef}
          className="min-h-[320px] overflow-auto p-6"
          style={backgroundStyle}
        >
          <div className="flex min-h-full min-w-full items-center justify-center">
            <img
              src={previewUrl}
              alt={metadata?.title || `Preview of ${fileName}`}
              draggable={false}
              onLoad={(event) => {
                const image = event.currentTarget;
                setNaturalSize({
                  width: image.naturalWidth || 300,
                  height: image.naturalHeight || 150,
                });
              }}
              onError={() => setError("The browser could not render this SVG.")}
              style={{
                width: Math.max(1, naturalSize.width * displayScale),
                height: Math.max(1, naturalSize.height * displayScale),
                maxWidth: "none",
                flex: "none",
              }}
            />
          </div>
        </div>

        <aside className="flex flex-col gap-4 border-t border-[var(--p-border)] bg-[var(--p-surface-2)] p-4 md:border-l md:border-t-0">
          <section>
            <div className="mb-2 font-mono text-[10px] uppercase tracking-wider text-[var(--p-muted)]">
              document
            </div>
            <div className="rounded-[12px] border border-[var(--p-border)] bg-[var(--p-surface)] p-3">
              <div className="truncate font-sans text-sm font-semibold text-[var(--p-text)]">
                {fileName}
              </div>
              <dl className="mt-3 grid grid-cols-[auto_1fr] gap-x-3 gap-y-2 font-mono text-[11px]">
                <dt className="text-[var(--p-muted)]">Size</dt>
                <dd className="m-0 text-right text-[var(--p-text)]">{formatBytes(sourceSize)}</dd>
                <dt className="text-[var(--p-muted)]">Width</dt>
                <dd className="m-0 text-right text-[var(--p-text)]">{metadata?.width || "auto"}</dd>
                <dt className="text-[var(--p-muted)]">Height</dt>
                <dd className="m-0 text-right text-[var(--p-text)]">{metadata?.height || "auto"}</dd>
                <dt className="text-[var(--p-muted)]">ViewBox</dt>
                <dd className="m-0 truncate text-right text-[var(--p-text)]" title={metadata?.viewBox}>
                  {metadata?.viewBox || "none"}
                </dd>
                <dt className="text-[var(--p-muted)]">Elements</dt>
                <dd className="m-0 text-right text-[var(--p-text)]">{metadata?.elementCount ?? 0}</dd>
              </dl>
            </div>
          </section>

          <section className="flex flex-col gap-2">
            <Button variant="secondary" onClick={() => setShowSource((value) => !value)}>
              {showSource ? "Hide source" : "View source"}
            </Button>
            <Button
              variant="secondary"
              onClick={async () => {
                await navigator.clipboard.writeText(source);
                setCopied(true);
                window.setTimeout(() => setCopied(false), 1400);
              }}
            >
              {copied ? "Copied" : "Copy SVG"}
            </Button>
            <Button onClick={() => downloadSvg(source, fileName)}>
              Download SVG
            </Button>
          </section>

          <p className="mt-auto font-mono text-[10px] leading-relaxed text-[var(--p-muted)]">
            Previewed locally as an image. SVG scripts are not executed in the page.
          </p>
        </aside>
      </div>

      {showSource && (
        <div className="border-t border-[var(--p-border)] bg-[var(--p-surface)] p-4">
          <label className="mb-2 block font-mono text-[10px] uppercase tracking-wider text-[var(--p-muted)]">
            SVG source
          </label>
          <textarea
            value={draft}
            onChange={(event) => setDraft(event.target.value)}
            spellCheck={false}
            rows={10}
            className="w-full resize-y rounded-[12px] border border-[var(--p-border)] bg-[var(--p-bg)] p-3 font-mono text-[12px] leading-relaxed text-[var(--p-text)] outline-none focus:border-[var(--p-accent)]"
          />
          <div className="mt-3 flex items-center justify-between gap-3">
            <span className="font-sans text-[12px] text-red-600">{error}</span>
            <Button onClick={() => applySource()}>Apply source</Button>
          </div>
        </div>
      )}
    </div>
  );
}
