"use client";

import { useState, useEffect, useRef, Fragment, Suspense } from "react";
import { useSearchParams } from "next/navigation";
import { useQuery, useMutation } from "@tanstack/react-query";
import { Button, Chip, Modal, Spinner } from "@heroui/react";
import {
  ThunderboltFill,
  PlugConnection,
  CreditCard,
  Check,
  QrCode,
  Xmark,
} from "@gravity-ui/icons";
import jsQR from "jsqr";
import { api } from "@/lib/api";
import dayjs from "@/lib/dayjs";

// ── Status maps (same as charge-points page) ────────────────────────────────

const statusLabelMap: Record<string, string> = {
  Available: "空闲中",
  Charging: "充电中",
  Preparing: "准备中",
  Finishing: "结束中",
  SuspendedEV: "EV 暂停",
  SuspendedEVSE: "EVSE 暂停",
  Reserved: "已预约",
  Faulted: "故障",
  Unavailable: "不可用",
  Occupied: "占用",
};

const statusDotClass: Record<string, string> = {
  Available: "bg-success",
  Charging: "bg-accent animate-pulse",
  Preparing: "bg-warning animate-pulse",
  Finishing: "bg-warning",
  SuspendedEV: "bg-warning",
  SuspendedEVSE: "bg-warning",
  Reserved: "bg-warning",
  Faulted: "bg-danger",
  Unavailable: "bg-danger",
  Occupied: "bg-warning",
};

// ── Step indicator ───────────────────────────────────────────────────────────

function StepBar({ step, onGoBack }: { step: number; onGoBack: (target: number) => void }) {
  const labels = ["选择充电桩", "选择充电口", "选择储值卡"];
  return (
    <div className="flex w-full items-start">
      {labels.map((label, i) => {
        const idx = i + 1;
        const isActive = step === idx;
        const isDone = idx < step;
        const isLast = i === labels.length - 1;
        return (
          <Fragment key={i}>
            <button
              type="button"
              onClick={() => isDone && onGoBack(idx)}
              disabled={!isDone}
              className={[
                "flex shrink-0 flex-col items-center gap-2",
                isDone ? "cursor-pointer" : "cursor-default",
              ].join(" ")}
            >
              <span
                className={[
                  "flex size-7 items-center justify-center rounded-full text-xs font-semibold ring-2 ring-offset-2 ring-offset-background transition-all",
                  isActive
                    ? "bg-accent text-accent-foreground ring-accent"
                    : isDone
                      ? "bg-success text-white ring-success"
                      : "bg-surface-tertiary text-muted ring-transparent",
                ].join(" ")}
              >
                {isDone ? <Check className="size-3.5" /> : idx}
              </span>
              <span
                className={[
                  "text-[11px] font-medium leading-none whitespace-nowrap",
                  isActive ? "text-accent" : isDone ? "text-foreground" : "text-muted",
                ].join(" ")}
              >
                {label}
              </span>
            </button>
            {!isLast && (
              <div className="flex-1 pt-3.5">
                <span
                  className={[
                    "block h-px w-full transition-colors",
                    isDone ? "bg-success" : "bg-border",
                  ].join(" ")}
                />
              </div>
            )}
          </Fragment>
        );
      })}
    </div>
  );
}

// ── QR Scanner ───────────────────────────────────────────────────────────────

type ScannerProps = {
  onResult: (raw: string) => void;
  onClose: () => void;
};

function QrScanner({ onResult, onClose }: ScannerProps) {
  const videoRef = useRef<HTMLVideoElement>(null);
  const streamRef = useRef<MediaStream | null>(null);
  const scanningRef = useRef(true);
  const mountedRef = useRef(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    mountedRef.current = true;
    scanningRef.current = true;
    let detector: any = null;

    async function start() {
      let stream: MediaStream;
      try {
        stream = await navigator.mediaDevices.getUserMedia({
          video: { facingMode: "environment" },
        });
      } catch (err: any) {
        if (mountedRef.current) setError("无法访问摄像头：" + (err?.message ?? "未知错误"));
        return;
      }

      if (!mountedRef.current) {
        stream.getTracks().forEach((t) => t.stop());
        return;
      }

      streamRef.current = stream;

      if (videoRef.current) {
        videoRef.current.srcObject = stream;
        try {
          await videoRef.current.play();
        } catch (err: any) {
          // AbortError fires when the element is removed mid-play (e.g. Modal animation).
          // It is not a real error — just bail out silently.
          if (err?.name === "AbortError") return;
          if (mountedRef.current) setError("无法播放摄像头画面：" + (err?.message ?? "未知错误"));
          return;
        }
      }

      if (!mountedRef.current) return;

      const useNative = "BarcodeDetector" in window;
      if (useNative) {
        detector = new (window as any).BarcodeDetector({ formats: ["qr_code"] });
      }

      const canvas = document.createElement("canvas");
      const ctx = canvas.getContext("2d");

      const scan = async () => {
        if (!scanningRef.current || !videoRef.current) return;
        try {
          if (useNative) {
            const codes: Array<{ rawValue: string }> = await detector.detect(videoRef.current);
            if (codes.length > 0) {
              onResult(codes[0].rawValue);
              return;
            }
          } else if (ctx) {
            const video = videoRef.current;
            canvas.width = video.videoWidth;
            canvas.height = video.videoHeight;
            ctx.drawImage(video, 0, 0);
            const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
            const code = jsQR(imageData.data, imageData.width, imageData.height);
            if (code) {
              onResult(code.data);
              return;
            }
          }
        } catch {}
        requestAnimationFrame(scan);
      };
      requestAnimationFrame(scan);
    }

    start();

    return () => {
      mountedRef.current = false;
      scanningRef.current = false;
      streamRef.current?.getTracks().forEach((t) => t.stop());
      streamRef.current = null;
    };
  }, [onResult]);

  return (
    <div className="relative h-full w-full overflow-hidden bg-black">
      {error ? (
        <div className="flex h-full flex-col items-center justify-center gap-3 p-6 text-center">
          <p className="text-sm text-danger">{error}</p>
        </div>
      ) : (
        <>
          {/* eslint-disable-next-line jsx-a11y/media-has-caption */}
          <video ref={videoRef} className="h-full w-full object-cover" playsInline muted />
          {/* Aim overlay */}
          <div className="pointer-events-none absolute inset-0 flex items-center justify-center">
            <div className="size-56 rounded-2xl border-2 border-white/80 shadow-[0_0_0_9999px_rgba(0,0,0,0.45)]" />
          </div>
          <p className="absolute bottom-8 left-0 right-0 text-center text-sm text-white/80">
            将二维码对准框内
          </p>
        </>
      )}
      {/* Close button — overlaid top-right */}
      <button
        type="button"
        onClick={onClose}
        className="absolute right-4 top-4 flex size-9 items-center justify-center rounded-full bg-black/50 text-white backdrop-blur-sm hover:bg-black/70"
        aria-label="关闭扫描"
      >
        <Xmark className="size-5" />
      </button>
    </div>
  );
}

// ── Main Page ────────────────────────────────────────────────────────────────

function ChargePageContent() {
  const searchParams = useSearchParams();

  const [step, setStep] = useState(1);
  const [selectedCpId, setSelectedCpId] = useState<string | null>(null);
  const [selectedConnectorId, setSelectedConnectorId] = useState<number | null>(null);
  const [selectedIdTag, setSelectedIdTag] = useState<string | null>(null);
  const [showScanner, setShowScanner] = useState(false);
  const [scanError, setScanError] = useState<string | null>(null);
  const [startResult, setStartResult] = useState<"success" | "error" | null>(null);
  const [startError, setStartError] = useState<string | null>(null);

  // Detect mobile
  const [isMobile, setIsMobile] = useState(false);
  useEffect(() => {
    setIsMobile(/Mobi|Android|iPhone|iPad/i.test(navigator.userAgent));
  }, []);

  // Pre-fill from URL params (QR code redirect)
  useEffect(() => {
    const cpId = searchParams.get("cpId");
    const connector = searchParams.get("connector");
    if (cpId) {
      setSelectedCpId(cpId);
      if (connector && !Number.isNaN(Number(connector))) {
        setSelectedConnectorId(Number(connector));
        setStep(3);
      } else {
        setStep(2);
      }
    }
  }, [searchParams]);

  const { data: chargePoints = [], isLoading: cpLoading } = useQuery({
    queryKey: ["chargePoints"],
    queryFn: () => api.chargePoints.list().catch(() => []),
    refetchInterval: 5_000,
  });

  const { data: idTags = [], isLoading: tagsLoading } = useQuery({
    queryKey: ["idTags"],
    queryFn: () => api.idTags.list().catch(() => []),
  });

  const selectedCp = chargePoints.find((cp) => cp.id === selectedCpId) ?? null;
  const myTags = idTags?.filter((t) => t.status === "Accepted") ?? [];

  const startMutation = useMutation({
    mutationFn: async () => {
      if (!selectedCp || selectedConnectorId === null || !selectedIdTag) {
        throw new Error("请先完成所有选择");
      }
      return api.transactions.remoteStart({
        chargePointIdentifier: selectedCp.chargePointIdentifier,
        connectorId: selectedConnectorId,
        idTag: selectedIdTag,
      });
    },
    onSuccess: () => {
      setStartResult("success");
    },
    onError: (err: Error) => {
      setStartResult("error");
      const msg = err.message ?? "";
      if (msg.includes("offline")) setStartError("充电桩当前不在线，请稍后再试");
      else if (msg.includes("not accepted")) setStartError("充电桩未启用，请联系管理员");
      else if (msg.includes("idTag")) setStartError("储值卡无效或无权使用");
      else setStartError("启动失败：" + msg);
    },
  });

  function resetAll() {
    setStep(1);
    setSelectedCpId(null);
    setSelectedConnectorId(null);
    setSelectedIdTag(null);
    setStartResult(null);
    setStartError(null);
  }

  function handleScanResult(raw: string) {
    setShowScanner(false);
    setScanError(null);
    try {
      // Support both helios:// scheme and https:// web URLs
      const url = new URL(raw);
      const cpId = url.searchParams.get("cpId");
      const connector = url.searchParams.get("connector");
      if (!cpId) {
        setScanError("二维码内容无效，缺少充电桩信息");
        return;
      }
      setSelectedCpId(cpId);
      if (connector && !Number.isNaN(Number(connector))) {
        setSelectedConnectorId(Number(connector));
        setStep(3);
      } else {
        setSelectedConnectorId(null);
        setStep(2);
      }
    } catch {
      setScanError("二维码内容格式错误");
    }
  }

  // ── Success screen ─────────────────────────────────────────────────────────
  if (startResult === "success") {
    return (
      <div className="flex flex-col items-center justify-center gap-6 py-20 text-center">
        <div className="flex size-16 items-center justify-center rounded-full bg-success/10">
          <Check className="size-8 text-success" />
        </div>
        <div>
          <h2 className="text-xl font-semibold text-foreground">充电指令已发送</h2>
          <p className="mt-1.5 text-sm text-muted">
            充电桩正在响应，充电将自动开始
            <br />
            你可以在"充电记录"中查看进度
          </p>
        </div>
        <Button onPress={resetAll}>再次充电</Button>
      </div>
    );
  }

  // ── Main UI ────────────────────────────────────────────────────────────────
  return (
    <div className="space-y-6">
      {/* Header */}
      <div className="flex flex-wrap items-start justify-between gap-3">
        <div>
          <h1 className="text-xl font-semibold text-foreground">立即充电</h1>
          <p className="mt-0.5 text-sm text-muted">选择充电桩和储值卡，远程启动充电</p>
        </div>

        {/* QR scan button — mobile only */}
        {isMobile && (
          <Button
            size="sm"
            variant="secondary"
            onPress={() => {
              setScanError(null);
              setShowScanner(true);
            }}
            isDisabled={showScanner}
          >
            <QrCode className="size-4" />
            扫码充电
          </Button>
        )}
      </div>

      {/* QR Scanner Modal */}
      <Modal
        isOpen={showScanner}
        onOpenChange={(open) => {
          if (!open) setShowScanner(false);
        }}
      >
        <Modal.Backdrop>
          <Modal.Container scroll="outside" size="full">
            <Modal.Dialog className="h-full overflow-hidden p-0">
              <QrScanner onResult={handleScanResult} onClose={() => setShowScanner(false)} />
            </Modal.Dialog>
          </Modal.Container>
        </Modal.Backdrop>
      </Modal>

      {scanError && <p className="text-sm text-danger">{scanError}</p>}

      {/* Step bar */}
      <StepBar step={step} onGoBack={(t) => setStep(t)} />

      {/* ── Step 1: Select charge point ──────────────────────────────── */}
      {step === 1 && (
        <div className="space-y-3">
          {cpLoading ? (
            <div className="flex justify-center py-12">
              <Spinner />
            </div>
          ) : chargePoints.filter((cp) => cp.registrationStatus === "Accepted").length === 0 ? (
            <div className="rounded-xl border border-border px-6 py-12 text-center">
              <PlugConnection className="mx-auto mb-2 size-8 text-muted" />
              <p className="text-sm text-muted">暂无可用充电桩</p>
            </div>
          ) : (
            <div className="grid gap-2 sm:grid-cols-2 lg:grid-cols-3">
              {chargePoints
                .filter((cp) => cp.registrationStatus === "Accepted")
                .map((cp) => {
                  const online =
                    !!cp.lastHeartbeatAt && dayjs().diff(dayjs(cp.lastHeartbeatAt), "second") < 120;
                  const availableCount = cp.connectors.filter(
                    (c) => c.status === "Available",
                  ).length;
                  const disabled = !online || availableCount === 0;
                  return (
                    <button
                      key={cp.id}
                      type="button"
                      disabled={disabled}
                      onClick={() => {
                        setSelectedCpId(cp.id);
                        setSelectedConnectorId(null);
                        setStep(2);
                      }}
                      className={[
                        "flex flex-col gap-2.5 rounded-xl border p-4 text-left transition-all",
                        disabled
                          ? "cursor-not-allowed opacity-50 border-border"
                          : "cursor-pointer border-border hover:border-accent hover:bg-accent/5",
                        selectedCpId === cp.id ? "border-accent bg-accent/10" : "",
                      ].join(" ")}
                    >
                      <div className="flex items-center justify-between gap-2">
                        <span className="font-medium text-foreground truncate">
                          {cp.chargePointIdentifier}
                        </span>
                        <Chip size="sm" color={online ? "success" : "default"} variant="soft">
                          {online ? "在线" : "离线"}
                        </Chip>
                      </div>
                      {(cp.chargePointVendor || cp.chargePointModel) && (
                        <span className="text-xs text-muted">
                          {[cp.chargePointVendor, cp.chargePointModel].filter(Boolean).join(" · ")}
                        </span>
                      )}
                      <div className="flex flex-wrap items-center gap-2">
                        <span className="flex items-center gap-1 text-xs text-muted">
                          <PlugConnection className="size-3" />
                          {availableCount}/{cp.connectors.length} 接口空闲
                        </span>
                        {cp.feePerKwh > 0 && (
                          <span className="text-xs text-muted">
                            · ¥{(cp.feePerKwh / 100).toFixed(2)}/kWh
                          </span>
                        )}
                        {cp.feePerKwh === 0 && <span className="text-xs text-success">· 免费</span>}
                      </div>
                    </button>
                  );
                })}
            </div>
          )}
        </div>
      )}

      {/* ── Step 2: Select connector ──────────────────────────────────── */}
      {step === 2 && (
        <div className="space-y-3">
          {selectedCp && (
            <p className="text-sm text-muted">
              充电桩：
              <span className="font-medium text-foreground">
                {selectedCp.chargePointIdentifier}
              </span>
            </p>
          )}

          {selectedCp && selectedCp.connectors.filter((c) => c.connectorId > 0).length === 0 ? (
            <div className="rounded-xl border border-border px-6 py-12 text-center">
              <PlugConnection className="mx-auto mb-2 size-8 text-muted" />
              <p className="text-sm text-muted">该充电桩暂无接口信息</p>
            </div>
          ) : (
            <div className="grid gap-2 grid-cols-2 sm:grid-cols-3 lg:grid-cols-4">
              {selectedCp?.connectors
                .filter((c) => c.connectorId > 0)
                .sort((a, b) => a.connectorId - b.connectorId)
                .map((conn) => {
                  const available = conn.status === "Available";
                  return (
                    <button
                      key={conn.id}
                      type="button"
                      disabled={!available}
                      onClick={() => {
                        if (available) {
                          setSelectedConnectorId(conn.connectorId);
                          setStep(3);
                        }
                      }}
                      className={[
                        "flex flex-col gap-2 rounded-xl border p-4 text-left transition-all",
                        !available
                          ? "cursor-not-allowed opacity-50 border-border"
                          : "cursor-pointer border-border hover:border-accent hover:bg-accent/5",
                        selectedConnectorId === conn.connectorId
                          ? "border-accent bg-accent/10"
                          : "",
                      ].join(" ")}
                    >
                      <div className="flex items-center justify-between">
                        <span className="font-medium text-foreground">
                          接口 #{conn.connectorId}
                        </span>
                        <span
                          className={`size-2 rounded-full ${statusDotClass[conn.status] ?? "bg-warning"}`}
                        />
                      </div>
                      <span className="text-xs text-muted">
                        {statusLabelMap[conn.status] ?? conn.status}
                      </span>
                    </button>
                  );
                })}
            </div>
          )}
        </div>
      )}

      {/* ── Step 3: Select ID tag + start ────────────────────────────── */}
      {step === 3 && (
        <div className="space-y-5">
          <div className="flex flex-wrap gap-3 text-sm text-muted">
            {selectedCp && (
              <span>
                充电桩：
                <span className="font-medium text-foreground">
                  {selectedCp.chargePointIdentifier}
                </span>
              </span>
            )}
            {selectedConnectorId !== null && (
              <span>
                接口：
                <span className="font-medium text-foreground">#{selectedConnectorId}</span>
              </span>
            )}
          </div>

          {tagsLoading ? (
            <div className="flex justify-center py-12">
              <Spinner />
            </div>
          ) : myTags.length === 0 ? (
            <div className="rounded-xl border border-border px-6 py-12 text-center">
              <CreditCard className="mx-auto mb-2 size-8 text-muted" />
              <p className="text-sm text-muted">你还没有可用的储值卡</p>
              <p className="mt-1 text-xs text-muted">请前往"储值卡"页面领取或添加</p>
            </div>
          ) : (
            <div className="grid gap-2 sm:grid-cols-2 lg:grid-cols-3">
              {myTags.map((tag) => (
                <button
                  key={tag.idTag}
                  type="button"
                  onClick={() => setSelectedIdTag(tag.idTag)}
                  className={[
                    "flex flex-col gap-1.5 rounded-xl border p-4 text-left transition-all cursor-pointer",
                    "border-border hover:border-accent hover:bg-accent/5",
                    selectedIdTag === tag.idTag ? "border-accent bg-accent/10" : "",
                  ].join(" ")}
                >
                  <div className="flex items-center justify-between">
                    <span className="font-mono text-sm font-medium text-foreground">
                      {tag.idTag}
                    </span>
                    {selectedIdTag === tag.idTag && (
                      <Check className="size-4 shrink-0 text-accent" />
                    )}
                  </div>
                  <div className="flex items-center gap-2">
                    <span className="text-xs text-muted">余额</span>
                    <span className="text-xs font-medium text-foreground">
                      ¥{(tag.balance / 100).toFixed(2)}
                    </span>
                  </div>
                </button>
              ))}
            </div>
          )}

          {startResult === "error" && (
            <p className="text-sm text-danger">{startError ?? "启动失败，请重试"}</p>
          )}

          <div className="flex gap-3">
            <Button
              variant="secondary"
              onPress={() => {
                setStep(2);
                setStartResult(null);
                setStartError(null);
              }}
            >
              返回
            </Button>
            <Button
              isDisabled={!selectedIdTag || startMutation.isPending}
              onPress={() => startMutation.mutate()}
            >
              {startMutation.isPending ? (
                <Spinner size="sm" />
              ) : (
                <ThunderboltFill className="size-4" />
              )}
              {startMutation.isPending ? "发送中…" : "启动充电"}
            </Button>
          </div>
        </div>
      )}
    </div>
  );
}

export default function ChargePage() {
  return (
    <Suspense>
      <ChargePageContent />
    </Suspense>
  );
}
