"use client";

import { useEffect, useRef, useState } from "react";
import { Eraser, ImageUp, Loader2, Paintbrush, Save, Upload } from "lucide-react";
import clsx from "clsx";
import { clampPage, pageItems, Pagination } from "@/components/pagination";
import { crossfadeIn, pulseFeedback, revealChildren, runScopedMotion } from "@/lib/ui/motion";
import type { Asset, GenerationJob } from "@/lib/types";

export type ImageEditMode = "inpaint" | "upscale";
const PICKER_PAGE_SIZE = 8;

export function ImageEditor({
  initialMode = "inpaint",
  hideModeSwitch = false
}: {
  initialMode?: ImageEditMode;
  hideModeSwitch?: boolean;
}) {
  const [assets, setAssets] = useState<Asset[]>([]);
  const [jobs, setJobs] = useState<GenerationJob[]>([]);
  const [selectedId, setSelectedId] = useState("");
  const [pickerOpen, setPickerOpen] = useState(false);
  const [pickerPage, setPickerPage] = useState(1);
  const [prompt, setPrompt] = useState("");
  const [brush, setBrush] = useState(42);
  const [mode, setMode] = useState<ImageEditMode>(initialMode);
  const [resolution, setResolution] = useState<"4k" | "8k">("4k");
  const [scale, setScale] = useState(50);
  const [busy, setBusy] = useState(false);
  const [notice, setNotice] = useState<string | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [uploading, setUploading] = useState(false);
  const editorRef = useRef<HTMLDivElement | null>(null);
  const pickerRef = useRef<HTMLDivElement | null>(null);
  const feedbackRef = useRef<HTMLDivElement | null>(null);
  const canvasRef = useRef<HTMLCanvasElement | null>(null);
  const imageRef = useRef<HTMLImageElement | null>(null);
  const drawing = useRef(false);

  const outputAssetIds = new Set(jobs.flatMap((job) => job.outputAssetIds));
  const imageAssets = assets.filter((asset) => isResultImageAsset(asset, outputAssetIds));
  const selected = assets.find((asset) => asset.id === selectedId && isEditableImageAsset(asset));
  const imageUrl = selected ? displayAssetUrl(selected.url) : undefined;
  const pickerTotal = imageAssets.length;
  const visibleImageAssets = pageItems(imageAssets, pickerPage, PICKER_PAGE_SIZE);

  async function refreshLibrary(nextSelectedId = selectedId) {
    const [assetResponse, imageJobResponse] = await Promise.all([
      fetch("/api/assets", { cache: "no-store" }),
      fetch("/api/generations/image", { cache: "no-store" })
    ]);
    const [assetPayload, jobPayload] = await Promise.all([assetResponse.json(), imageJobResponse.json()]);
    if (!assetResponse.ok) throw new Error(assetPayload.error || "读取资产失败");
    if (!imageJobResponse.ok) throw new Error(jobPayload.error || "读取图片任务失败");
    const nextAssets = assetPayload.assets || [];
    const nextImageAssets = nextAssets.filter(isEditableImageAsset);
    setAssets(nextAssets);
    setJobs(jobPayload.jobs || []);
    if (nextSelectedId && nextImageAssets.some((asset: Asset) => asset.id === nextSelectedId)) {
      setSelectedId(nextSelectedId);
    } else if (nextSelectedId) {
      setSelectedId("");
    }
  }

  useEffect(() => {
    refreshLibrary().catch((err) => setError(err instanceof Error ? err.message : String(err)));
  }, []);

  useEffect(() => {
    setMode(initialMode);
  }, [initialMode]);

  useEffect(() => {
    setPickerPage(1);
  }, [pickerOpen]);

  useEffect(() => {
    setPickerPage((page) => clampPage(page, pickerTotal, PICKER_PAGE_SIZE));
  }, [pickerTotal]);

  useEffect(() => {
    if (mode !== "inpaint" || !imageUrl) return;
    window.requestAnimationFrame(resetCanvas);
  }, [mode, imageUrl]);

  useEffect(() => {
    return runScopedMotion(editorRef, (scope) => revealChildren(scope));
  }, []);

  useEffect(() => {
    crossfadeIn(pickerRef.current);
  }, [pickerOpen, pickerPage]);

  useEffect(() => {
    pulseFeedback(feedbackRef.current);
  }, [error, notice]);

  function resetCanvas() {
    const canvas = canvasRef.current;
    const image = imageRef.current;
    if (!canvas || !image) return;
    const width = image.naturalWidth || 1024;
    const height = image.naturalHeight || 1024;
    canvas.width = width;
    canvas.height = height;
    const ctx = canvas.getContext("2d");
    if (!ctx) return;
    ctx.clearRect(0, 0, width, height);
  }

  function paint(event: React.PointerEvent<HTMLCanvasElement>) {
    const canvas = canvasRef.current;
    if (!canvas || !drawing.current) return;
    const rect = canvas.getBoundingClientRect();
    const x = ((event.clientX - rect.left) / rect.width) * canvas.width;
    const y = ((event.clientY - rect.top) / rect.height) * canvas.height;
    const ctx = canvas.getContext("2d");
    if (!ctx) return;
    ctx.fillStyle = "white";
    ctx.beginPath();
    ctx.arc(x, y, brush, 0, Math.PI * 2);
    ctx.fill();
  }

  async function runEdit() {
    setBusy(true);
    setError(null);
    setNotice(null);
    try {
      if (!selected) throw new Error("请选择一张图片资产，或先上传图片。");
      const endpoint = mode === "inpaint" ? `/api/assets/${selected.id}/inpaint` : `/api/assets/${selected.id}/upscale`;
      const body = mode === "inpaint"
        ? {
            prompt,
            maskDataUrl: buildMaskDataUrl(canvasRef.current)
          }
        : {
            resolution,
            scale
          };
      const response = await fetch(endpoint, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(body)
      });
      const payload = await response.json();
      if (!response.ok) throw new Error(payload.error || "提交失败");
      setNotice(`${mode === "inpaint" ? "局部重绘" : "超清"}任务已提交。请到「结果」里的任务查看进度，完成后结果资产会自动保留。`);
      setPrompt("");
      resetCanvas();
    } catch (err) {
      setError(err instanceof Error ? err.message : String(err));
    } finally {
      setBusy(false);
    }
  }

  async function uploadImage(files: FileList | null) {
    if (!files?.length) return;
    setUploading(true);
    setError(null);
    try {
      const formData = new FormData();
      formData.append("files", files[0]);
      const response = await fetch("/api/assets/upload", {
        method: "POST",
        body: formData
      });
      const payload = await response.json();
      if (!response.ok) throw new Error(payload.error || "上传失败");
      const uploaded = (payload.assets || []).find((asset: Asset) => isEditableImageAsset(asset)) as Asset | undefined;
      if (!uploaded) throw new Error("请上传图片文件。");
      setPickerOpen(false);
      await refreshLibrary(uploaded.id);
    } catch (err) {
      setError(err instanceof Error ? err.message : String(err));
    } finally {
      setUploading(false);
    }
  }

  return (
    <div className="grid cols-2" ref={editorRef}>
      <section className="panel" data-animate>
        <div className="panel-head">
          <div>
            <h2>选择素材与处理方式</h2>
          </div>
        </div>
        {!hideModeSwitch ? (
          <div className="segmented" style={{ marginBottom: 16 }}>
            <button className={mode === "inpaint" ? "active" : ""} aria-pressed={mode === "inpaint"} onClick={() => setMode("inpaint")}>局部重绘</button>
            <button className={mode === "upscale" ? "active" : ""} aria-pressed={mode === "upscale"} onClick={() => setMode("upscale")}>智能超清</button>
          </div>
        ) : null}
        <div className="selected-asset-line editor-selection-summary">
          {selected ? (
            <img src={displayAssetUrl(selected.url)} alt="" />
          ) : (
            <span className="selected-asset-placeholder"><ImageUp size={18} /></span>
          )}
          <div>
            <strong>{selected?.name || "未选择图片"}</strong>
            <span className="muted">{selected ? sourceLabel(selected.source) : "从资产任务选择，或直接上传图片"}</span>
          </div>
          <button className="button mini-button" type="button" onClick={() => setPickerOpen((open) => !open)}>
            {pickerOpen ? "收起" : selected ? "更换素材" : "选择素材"}
          </button>
          <label className="upload-icon-button image-upload-button" title="上传本地图片">
            {uploading ? <Loader2 className="spin" size={18} /> : <Upload size={18} />}
            <span>上传图片</span>
            <input
              type="file"
              accept="image/*"
              onChange={(event) => {
                void uploadImage(event.target.files);
                event.currentTarget.value = "";
              }}
            />
          </label>
        </div>

        {pickerOpen ? (
          <div className="editor-picker-panel" ref={pickerRef}>
            <div className="editor-picker-grid">
              {visibleImageAssets.map((asset) => (
                <button
                  className={clsx("editor-thumb-option", selectedId === asset.id && "selected")}
                  type="button"
                  key={asset.id}
                  onClick={() => {
                    setSelectedId(asset.id);
                    setPickerOpen(false);
                  }}
                  title={asset.name}
                >
                  <img src={displayAssetUrl(asset.url)} alt={asset.name} />
                  <span className="editor-thumb-info">
                    <strong>{asset.name}</strong>
                    <span>{sourceLabel(asset.source)}</span>
                  </span>
                </button>
              ))}
              {!imageAssets.length ? (
                <div className="callout editor-empty">暂无结果图片，可先生成图片，或直接上传图片。</div>
              ) : null}
            </div>
            <Pagination page={pickerPage} pageSize={PICKER_PAGE_SIZE} total={pickerTotal} label="选择素材分页" onPageChange={setPickerPage} />
          </div>
        ) : null}
        {mode === "inpaint" ? (
          <>
            <div className="field">
              <label htmlFor="editPrompt">重绘提示词</label>
              <input id="editPrompt" value={prompt} onChange={(event) => setPrompt(event.target.value)} />
            </div>
            <div className="field">
              <label htmlFor="brush">画笔大小：{brush}</label>
              <input id="brush" type="range" min="8" max="120" value={brush} onChange={(event) => setBrush(Number(event.target.value))} />
            </div>
          </>
        ) : (
          <>
            <div className="field">
              <label htmlFor="resolution">目标分辨率</label>
              <select id="resolution" value={resolution} onChange={(event) => setResolution(event.target.value === "8k" ? "8k" : "4k")}>
                <option value="4k">4K</option>
                <option value="8k">8K</option>
              </select>
            </div>
            <div className="field">
              <label htmlFor="upscaleScale">细节生成程度：{scale}</label>
              <input id="upscaleScale" type="range" min="0" max="100" value={scale} onChange={(event) => setScale(Number(event.target.value))} />
            </div>
          </>
        )}
        <div className="toolbar">
          <button className="button primary" onClick={runEdit} disabled={busy || !selected}>
            {busy ? <Loader2 className="spin" size={18} /> : mode === "inpaint" ? <Paintbrush size={18} /> : <ImageUp size={18} />}
            提交任务
          </button>
          {mode === "inpaint" ? (
            <button className="button" type="button" onClick={resetCanvas}>
              <Eraser size={18} />
              清空选区
            </button>
          ) : null}
        </div>
        {error || notice ? (
          <div ref={feedbackRef}>
            {error ? <div className="callout" role="alert" style={{ marginTop: 14 }}>{error}</div> : null}
            {notice ? <div className="callout success-callout" role="status" aria-live="polite" style={{ marginTop: 14 }}>{notice}</div> : null}
          </div>
        ) : null}
      </section>

      <section className="panel" data-animate>
        <h2>{mode === "inpaint" ? "黑色保留，白色重绘" : "素材增强"}</h2>
        {imageUrl ? (
          <div className="editor-canvas-wrap">
            <img ref={imageRef} src={imageUrl} alt="待处理素材" onLoad={resetCanvas} />
            {mode === "inpaint" ? (
              <canvas
                ref={canvasRef}
                onPointerDown={(event) => {
                  drawing.current = true;
                  event.currentTarget.setPointerCapture(event.pointerId);
                  paint(event);
                }}
                onPointerMove={paint}
                onPointerUp={() => {
                  drawing.current = false;
                }}
              />
            ) : null}
          </div>
        ) : (
          <div className="callout">选择素材后即可开始局部重绘或超清。</div>
        )}
        <p className="muted" style={{ marginTop: 14 }}>
          <Save size={15} style={{ verticalAlign: "text-bottom" }} /> 输出结果会自动保存到结果。
        </p>
      </section>
    </div>
  );
}

function isEditableImageAsset(asset: Asset) {
  if (asset.kind === "mask") return false;
  if (asset.tags.includes("mask")) return false;
  if (typeof asset.metadata.maskRule === "string") return false;
  const contentType = typeof asset.metadata.contentType === "string" ? asset.metadata.contentType.toLowerCase() : "";
  return asset.kind === "image" || asset.kind === "reference" || contentType.startsWith("image/");
}

function isResultImageAsset(asset: Asset, outputAssetIds: Set<string>) {
  if (!outputAssetIds.has(asset.id)) return false;
  if (!isEditableImageAsset(asset)) return false;
  return asset.source === "generated" || asset.source === "edited" || asset.source === "upscaled";
}

function sourceLabel(source: Asset["source"]) {
  if (source === "upload") return "上传";
  if (source === "generated") return "生成";
  if (source === "edited") return "重绘";
  if (source === "upscaled") return "超清";
  if (source === "seed") return "示例";
  return "外部";
}

function capabilityLabel(capability: GenerationJob["capability"]) {
  if (capability === "image.generate") return "图片生成";
  if (capability === "image.inpaint") return "局部重绘";
  if (capability === "image.upscale") return "智能超清";
  return "图片任务";
}

function buildMaskDataUrl(canvas: HTMLCanvasElement | null) {
  if (!canvas) return undefined;
  const mask = document.createElement("canvas");
  mask.width = canvas.width;
  mask.height = canvas.height;
  const ctx = mask.getContext("2d");
  if (!ctx) return undefined;
  ctx.fillStyle = "black";
  ctx.fillRect(0, 0, mask.width, mask.height);
  ctx.drawImage(canvas, 0, 0);
  return mask.toDataURL("image/png");
}

function displayAssetUrl(url: string) {
  if (typeof window === "undefined") return url;
  try {
    const parsed = new URL(url, window.location.origin);
    if (parsed.hostname === "0.0.0.0") {
      parsed.protocol = window.location.protocol;
      parsed.host = window.location.host;
      return parsed.toString();
    }
    return parsed.toString();
  } catch {
    return url;
  }
}
