import {
  ArrowRight,
  Clock3,
  CloudUpload,
  FileCode2,
  Package,
  Play,
  RadioTower,
  RefreshCw,
  ShieldCheck,
  SquareTerminal,
  X,
} from "lucide-react";
import { FormEvent, ReactNode, useState } from "react";
import { InvokeResult, RegisterComponentInput } from "../api";
import {
  FormattedInvocationOutput,
  Service,
  formatInvocationOutput,
  invocationInputSize,
  parseInvocationInput,
} from "../console-model";

function DialogFrame({
  title,
  description,
  icon,
  onClose,
  closeDisabled = false,
  children,
}: {
  title: string;
  description: string;
  icon: ReactNode;
  onClose: () => void;
  closeDisabled?: boolean;
  children: ReactNode;
}) {
  return (
    <div className="dialog-backdrop">
      <dialog
        open
        className="dialog"
        aria-label={title}
        onCancel={(event) => {
          if (closeDisabled) event.preventDefault();
          else onClose();
        }}
        onKeyDown={(event) => {
          if (event.key === "Escape" && !closeDisabled) onClose();
        }}
      >
        <div className="dialog-header">
          <div className="dialog-title">
            <span>{icon}</span>
            <div>
              <h2>{title}</h2>
              <p>{description}</p>
            </div>
          </div>
          <button
            className="icon-button"
            type="button"
            aria-label="关闭"
            title="关闭"
            disabled={closeDisabled}
            onClick={onClose}
          >
            <X size={18} />
          </button>
        </div>
        {children}
      </dialog>
    </div>
  );
}

export function ActivateDeploymentDialog({
  service,
  currentRevision,
  submitting,
  onClose,
  onConfirm,
}: {
  service: Service;
  currentRevision: string | null;
  submitting: boolean;
  onClose: () => void;
  onConfirm: () => void;
}) {
  return (
    <DialogFrame
      title="切换对外版本"
      description="更新该服务在 Gateway 上接收请求的活动版本。"
      icon={<RadioTower size={19} />}
      onClose={onClose}
      closeDisabled={submitting}
    >
      <div className="deployment-confirmation">
        <div className="deployment-route">
          <div>
            <span>当前版本</span>
            <strong>{currentRevision ?? "尚未部署"}</strong>
          </div>
          <ArrowRight size={18} />
          <div className="deployment-target">
            <span>目标版本</span>
            <strong>{service.revision}</strong>
          </div>
        </div>
        <p>
          Wasmeld 将先确认 {service.id}@{service.revision} 的 Actor 可用，再更新对外路由。
        </p>
      </div>
      <div className="dialog-footer">
        <button className="secondary-button" type="button" disabled={submitting} onClick={onClose}>
          取消
        </button>
        <button className="primary-button" type="button" disabled={submitting} onClick={onConfirm}>
          {submitting ? <RefreshCw className="spin" size={16} /> : <RadioTower size={16} />}
          {submitting ? "正在切换" : "确认切换"}
        </button>
      </div>
    </DialogFrame>
  );
}

export function DeployDialog({
  onClose,
  onRegister,
}: {
  onClose: () => void;
  onRegister: (input: RegisterComponentInput) => Promise<void>;
}) {
  const [file, setFile] = useState<File | null>(null);
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState("");
  const fileName = file?.name ?? "";

  async function submit(event: FormEvent) {
    event.preventDefault();
    if (!file) return;
    if (!file.name.toLowerCase().endsWith(".wasmpkg")) {
      setError("请选择 .wasmpkg 组件包");
      return;
    }
    setSubmitting(true);
    setError("");
    try {
      await onRegister({ file });
    } catch (submitError) {
      setError(submitError instanceof Error ? submitError.message : "组件注册失败");
      setSubmitting(false);
    }
  }

  return (
    <DialogFrame
      title="注册组件包"
      description="组件元数据与运行边界由平台自动校验。"
      icon={<CloudUpload size={19} />}
      onClose={onClose}
    >
      <form className="dialog-form" onSubmit={submit}>
        <label className={fileName ? "upload-zone has-file" : "upload-zone"}>
          <input
            type="file"
            accept=".wasmpkg,application/zip,application/vnd.wasm.component-package"
            onChange={(event) => {
              setFile(event.target.files?.[0] ?? null);
              setError("");
            }}
          />
          {fileName ? <Package size={28} /> : <CloudUpload size={28} />}
          <strong>{fileName || "选择组件包"}</strong>
          <span>
            {file
              ? `${(file.size / 1024 / 1024).toFixed(2)} MB · 等待校验`
              : ".wasmpkg · 最大 64 MB"}
          </span>
        </label>

        {error && <div className="form-error">{error}</div>}

        <div className="dialog-footer">
          <button className="secondary-button" type="button" onClick={onClose}>
            取消
          </button>
          <button className="primary-button" type="submit" disabled={submitting || !fileName}>
            {submitting ? <RefreshCw className="spin" size={16} /> : <ShieldCheck size={16} />}
            {submitting ? "正在注册" : "校验并注册"}
          </button>
        </div>
      </form>
    </DialogFrame>
  );
}

export function WitPackageDialog({
  onClose,
  onPublish,
}: {
  onClose: () => void;
  onPublish: (file: File) => Promise<void>;
}) {
  const [file, setFile] = useState<File | null>(null);
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState("");
  const fileName = file?.name ?? "";

  async function submit(event: FormEvent) {
    event.preventDefault();
    if (!file) return;
    if (!file.name.toLowerCase().endsWith(".wasm")) {
      setError("请选择由 wasmeld wit build 生成的 .wasm 文件");
      return;
    }
    if (file.size > 4 * 1024 * 1024) {
      setError("WIT 包不能超过 4 MB");
      return;
    }
    setSubmitting(true);
    setError("");
    try {
      await onPublish(file);
    } catch (submitError) {
      setError(submitError instanceof Error ? submitError.message : "WIT 包发布失败");
      setSubmitting(false);
    }
  }

  return (
    <DialogFrame
      title="发布 WIT 包"
      description="包名、语义版本和直接依赖将从二进制 WIT Package 读取。"
      icon={<FileCode2 size={19} />}
      onClose={onClose}
    >
      <form className="dialog-form" onSubmit={submit}>
        <label className={fileName ? "upload-zone has-file" : "upload-zone"}>
          <input
            type="file"
            accept=".wasm,application/wasm"
            onChange={(event) => {
              setFile(event.target.files?.[0] ?? null);
              setError("");
            }}
          />
          {fileName ? <FileCode2 size={28} /> : <CloudUpload size={28} />}
          <strong>{fileName || "选择二进制 WIT Package"}</strong>
          <span>
            {file ? `${(file.size / 1024).toFixed(1)} KB · 等待校验` : ".wasm · 最大 4 MB"}
          </span>
        </label>

        {error && <div className="form-error">{error}</div>}

        <div className="dialog-footer">
          <button className="secondary-button" type="button" onClick={onClose}>
            取消
          </button>
          <button className="primary-button" type="submit" disabled={submitting || !fileName}>
            {submitting ? <RefreshCw className="spin" size={16} /> : <ShieldCheck size={16} />}
            {submitting ? "正在发布" : "校验并发布"}
          </button>
        </div>
      </form>
    </DialogFrame>
  );
}

export function InvokeDialog({
  service,
  onClose,
  onInvoke,
}: {
  service: Service;
  onClose: () => void;
  onInvoke: (service: Service, input: Uint8Array) => Promise<InvokeResult>;
}) {
  const [input, setInput] = useState(service.id === "echo" ? "hello wasm" : "");
  const [format, setFormat] = useState<"utf8" | "hex">("utf8");
  const [output, setOutput] = useState("");
  const [outputBytes, setOutputBytes] = useState<number | null>(null);
  const [outputFormat, setOutputFormat] = useState<FormattedInvocationOutput | null>(null);
  const [running, setRunning] = useState(false);
  const [failed, setFailed] = useState(false);
  const [latency, setLatency] = useState<number | null>(null);

  async function invoke() {
    setRunning(true);
    setOutput("");
    setOutputBytes(null);
    setOutputFormat(null);
    setFailed(false);
    setLatency(null);
    try {
      const result = await onInvoke(service, parseInvocationInput(input, format));
      const formatted = formatInvocationOutput(service, result.output, format);
      setOutput(formatted.text);
      setOutputBytes(result.outputBytes);
      setOutputFormat(formatted);
      setLatency(result.latencyMs);
    } catch (invokeError) {
      setFailed(true);
      setOutput(invokeError instanceof Error ? invokeError.message : "组件调用失败");
    } finally {
      setRunning(false);
    }
  }

  return (
    <DialogFrame
      title={`调用 ${service.id}@${service.revision}`}
      description="请求将进入该实例的串行 mailbox。"
      icon={<SquareTerminal size={19} />}
      onClose={onClose}
    >
      <div className="invoke-body">
        <div className="invoke-toolbar">
          <div className="segmented" aria-label="输入编码">
            <button
              type="button"
              className={format === "utf8" ? "active" : ""}
              onClick={() => setFormat("utf8")}
            >
              UTF-8
            </button>
            <button
              type="button"
              className={format === "hex" ? "active" : ""}
              onClick={() => setFormat("hex")}
            >
              HEX
            </button>
          </div>
          <span>{invocationInputSize(input, format)} B</span>
        </div>
        <label className="code-field">
          <span>输入</span>
          <textarea
            value={input}
            onChange={(event) => setInput(event.target.value)}
            spellCheck={false}
            placeholder={format === "utf8" ? "输入请求内容" : "00 ff a1"}
          />
        </label>

        <div className={failed ? "invoke-output failed" : "invoke-output"}>
          <div>
            <span>输出</span>
            {output && latency !== null && outputBytes !== null && outputFormat && (
              <div className="invoke-output-meta">
                <span>
                  {outputBytes} B · {outputFormat.format}
                  {outputFormat.automatic ? " · 自动" : ""}
                </span>
                <small>
                  <Clock3 size={13} />
                  {latency} ms
                </small>
              </div>
            )}
          </div>
          <pre>{running ? "invoking..." : output || "等待调用"}</pre>
        </div>
      </div>
      <div className="dialog-footer">
        <button className="secondary-button" type="button" onClick={onClose}>
          关闭
        </button>
        <button className="primary-button" type="button" disabled={running} onClick={invoke}>
          {running ? <RefreshCw className="spin" size={16} /> : <Play size={16} />}
          执行调用
        </button>
      </div>
    </DialogFrame>
  );
}
