import { createFileRoute, Link, useNavigate } from "@tanstack/react-router";
import { useEffect, useRef, useState } from "react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { supabase } from "@/integrations/supabase/client";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { ArrowLeft, Camera, CameraOff, ClipboardCheck, MapPin, QrCode, Search, ScanFace, RefreshCw } from "lucide-react";
import { toast } from "sonner";
import { Html5Qrcode } from "html5-qrcode";

export const Route = createFileRoute("/_authenticated/attendance/new")({
  component: () => <NewCheckInPage />,
});

type Coords = { lat: number; lng: number; accuracy?: number };

function detectShiftFromNow(): "A" | "B" | "C" {
  const h = new Date().getHours();
  if (h >= 6 && h < 14) return "A";
  if (h >= 14 && h < 22) return "B";
  return "C";
}

function getPosition(): Promise<Coords> {
  return new Promise((resolve, reject) => {
    if (!("geolocation" in navigator)) return reject(new Error("Geolocation not supported"));
    navigator.geolocation.getCurrentPosition(
      (p) => resolve({ lat: p.coords.latitude, lng: p.coords.longitude, accuracy: p.coords.accuracy }),
      (e) => reject(new Error(e.message)),
      { enableHighAccuracy: true, timeout: 10000 },
    );
  });
}

export function NewCheckInPage({ embedded = false, onDone }: { embedded?: boolean; onDone?: () => void } = {}) {
  const qc = useQueryClient();
  const navigate = useNavigate();
  const [guardId, setGuardId] = useState("");
  const [idSearch, setIdSearch] = useState("");
  const [method, setMethod] = useState<"qr" | "gps" | "face">("gps");
  const [qrCode, setQrCode] = useState("");
  const [shift, setShift] = useState<"A" | "B" | "C" | "custom">(detectShiftFromNow());
  const [coords, setCoords] = useState<Coords | null>(null);
  const todayIso = () => {
    const d = new Date();
    const pad = (n: number) => String(n).padStart(2, "0");
    return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
  };
  const [checkInAt, setCheckInAt] = useState<string>(todayIso);
  const [locating, setLocating] = useState(false);
  const [scanning, setScanning] = useState(false);
  const scannerRef = useRef<Html5Qrcode | null>(null);
  const scannerElId = "qr-reader-new";

  // Face capture state
  const videoRef = useRef<HTMLVideoElement | null>(null);
  const faceStreamRef = useRef<MediaStream | null>(null);
  const [faceCamOn, setFaceCamOn] = useState(false);
  const [facePhoto, setFacePhoto] = useState<{ blob: Blob; url: string } | null>(null);

  const guardsQ = useQuery({
    queryKey: ["guards", "min"],
    queryFn: async () => {
      const { data, error } = await supabase
        .from("security_guards")
        .select("id, full_name, employee_id")
        .order("full_name");
      if (error) throw error;
      return data;
    },
  });

  const stopScanner = async () => {
    try {
      if (scannerRef.current) {
        const state = scannerRef.current.getState();
        if (state === 2) await scannerRef.current.stop();
        await scannerRef.current.clear();
      }
    } catch { /* noop */ }
    scannerRef.current = null;
    setScanning(false);
  };

  const handleDecodedQr = (decoded: string) => {
    const txt = decoded.trim();
    const m = /^GUARD:(.+)$/i.exec(txt);
    if (m) {
      const empId = m[1].trim();
      const g = guardsQ.data?.find((x) => x.employee_id.toLowerCase() === empId.toLowerCase());
      if (g) { setGuardId(g.id); toast.success(`QR matched: ${g.full_name}`); return; }
      toast.error(`No guard with employee ID ${empId}`); return;
    }
    const p = /^POST:(.+)$/i.exec(txt);
    if (p) { toast.success(`Post QR: ${p[1].trim()}`); return; }
    toast.success("QR scanned");
  };

  const startScanner = async () => {
    try {
      const inst = new Html5Qrcode(scannerElId);
      scannerRef.current = inst;
      setScanning(true);
      await inst.start(
        { facingMode: "environment" },
        { fps: 10, qrbox: 220 },
        (decoded) => { setQrCode(decoded); handleDecodedQr(decoded); void stopScanner(); },
        () => { /* ignore */ },
      );
    } catch (e) {
      toast.error(e instanceof Error ? e.message : "Camera unavailable");
      setScanning(false);
    }
  };

  useEffect(() => () => { void stopScanner(); }, []);
  useEffect(() => { if (method !== "qr" && scanning) void stopScanner(); }, [method, scanning]);

  const stopFaceCam = () => {
    faceStreamRef.current?.getTracks().forEach((t) => t.stop());
    faceStreamRef.current = null;
    if (videoRef.current) videoRef.current.srcObject = null;
    setFaceCamOn(false);
  };
  const startFaceCam = async () => {
    try {
      const stream = await navigator.mediaDevices.getUserMedia({
        video: { facingMode: "user", width: 480, height: 480 },
        audio: false,
      });
      faceStreamRef.current = stream;
      setFaceCamOn(true);
      setTimeout(() => {
        if (videoRef.current) {
          videoRef.current.srcObject = stream;
          void videoRef.current.play();
        }
      }, 50);
    } catch (e) {
      toast.error(e instanceof Error ? e.message : "Camera unavailable");
    }
  };
  const capturePhoto = async () => {
    const v = videoRef.current;
    if (!v || !v.videoWidth) return toast.error("Camera not ready");
    const canvas = document.createElement("canvas");
    const size = Math.min(v.videoWidth, v.videoHeight);
    canvas.width = size;
    canvas.height = size;
    const ctx = canvas.getContext("2d");
    if (!ctx) return;
    const sx = (v.videoWidth - size) / 2;
    const sy = (v.videoHeight - size) / 2;
    ctx.drawImage(v, sx, sy, size, size, 0, 0, size, size);
    const blob: Blob | null = await new Promise((res) => canvas.toBlob((b) => res(b), "image/jpeg", 0.85));
    if (!blob) return toast.error("Could not capture photo");
    if (facePhoto) URL.revokeObjectURL(facePhoto.url);
    setFacePhoto({ blob, url: URL.createObjectURL(blob) });
    stopFaceCam();
  };
  useEffect(() => () => { stopFaceCam(); if (facePhoto) URL.revokeObjectURL(facePhoto.url); }, []); // eslint-disable-line react-hooks/exhaustive-deps
  useEffect(() => { if (method !== "face") stopFaceCam(); }, [method]);

  const captureLocation = async () => {
    setLocating(true);
    try {
      const c = await getPosition();
      setCoords(c);
      toast.success(`GPS locked (±${Math.round(c.accuracy ?? 0)}m)`);
    } catch (e) {
      toast.error(e instanceof Error ? e.message : "GPS failed");
    } finally { setLocating(false); }
  };

  const checkIn = useMutation({
    mutationFn: async () => {
      if (!guardId) throw new Error("Select a guard");
      if (method === "qr" && !qrCode.trim()) throw new Error("Scan or paste QR code");
      if (method === "face" && !facePhoto) throw new Error("Capture a face photo first");
      const { data: existing, error: exErr } = await supabase
        .from("attendance").select("id").eq("guard_id", guardId).eq("status", "checked_in").limit(1);
      if (exErr) throw exErr;
      if (existing && existing.length > 0) throw new Error("Guard already has an open check-in");

      // Prevent duplicate attendance for the same guard, same date, same shift
      const dayStart = new Date(`${checkInAt}T00:00:00`);
      const dayEnd = new Date(dayStart);
      dayEnd.setDate(dayEnd.getDate() + 1);
      const { data: dupShift, error: dupErr } = await supabase
        .from("attendance")
        .select("id")
        .eq("guard_id", guardId)
        .eq("shift", shift)
        .gte("check_in_at", dayStart.toISOString())
        .lt("check_in_at", dayEnd.toISOString())
        .limit(1);
      if (dupErr) throw dupErr;
      if (dupShift && dupShift.length > 0) {
        throw new Error(`এই গার্ডের ${checkInAt} তারিখে Shift ${shift}-এ ইতিমধ্যে অ্যাটেনডেন্স রয়েছে`);
      }

      let pos: Coords | null = coords;
      if (!pos) {
        pos = await getPosition().catch((e) => { if (method === "gps") throw e; return null; });
      }
      const { data: u } = await supabase.auth.getUser();
      let facePath: string | null = null;
      if (method === "face" && facePhoto) {
        const path = `${guardId}/${Date.now()}.jpg`;
        const { error: upErr } = await supabase.storage
          .from("face-attendance")
          .upload(path, facePhoto.blob, { contentType: "image/jpeg", upsert: false });
        if (upErr) throw upErr;
        facePath = path;
      }
      const { error } = await supabase.from("attendance").insert({
        guard_id: guardId, method,
        qr_code: method === "qr" ? qrCode.trim() : null,
        check_in_lat: pos?.lat ?? null, check_in_lng: pos?.lng ?? null,
        location: null,
        status: "checked_in",
        shift, created_by: u.user?.id,
        face_photo_url: facePath,
        check_in_at: checkInAt ? new Date(`${checkInAt}T00:00:00`).toISOString() : new Date().toISOString(),
      });
      if (error) throw error;
    },
    onSuccess: () => {
      toast.success("Checked in");
      qc.invalidateQueries({ queryKey: ["attendance"] });
      if (embedded) {
        onDone?.();
      } else {
        navigate({ to: "/attendance" });
      }
    },
    onError: (e: Error) => toast.error(e.message),
  });

  return (
    <div className="space-y-6">
      {!embedded && (
        <div className="flex items-center justify-between gap-3">
          <Button asChild variant="ghost" size="sm">
            <Link to="/attendance"><ArrowLeft className="h-4 w-4 mr-1" /> Back to attendance</Link>
          </Button>
        </div>
      )}

      <Card className="shadow-elegant border-border/60 rounded-2xl overflow-hidden">
        <div className="h-1 w-full bg-gradient-primary" aria-hidden />
        <CardHeader>
          <div className="flex items-center gap-3">
            <div className="h-10 w-10 rounded-xl bg-gradient-primary grid place-items-center text-primary-foreground shadow-elegant">
              <ClipboardCheck className="h-5 w-5" />
            </div>
            <div>
              <CardTitle>New Check-in</CardTitle>
              <CardDescription>Verify guard presence with QR or GPS</CardDescription>
            </div>
          </div>
        </CardHeader>
        <CardContent className="space-y-4">
          {/* Category: Guard & Assignment */}
          <div className="rounded-xl border border-border/60 bg-muted/20 p-4 space-y-3">
            <div className="flex items-center gap-2">
              <ClipboardCheck className="h-4 w-4 text-primary" />
              <h3 className="text-sm font-semibold">Guard &amp; Assignment</h3>
            </div>
            <div className="grid gap-4">
            <div className="grid gap-2">
              <Label>Guard</Label>
              <div className="relative">
                <Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
                <Input
                  value={idSearch}
                  onChange={(e) => {
                    const v = e.target.value;
                    setIdSearch(v);
                    const q = v.trim().toLowerCase();
                    if (!q) return;
                    const match = guardsQ.data?.find((g) => g.employee_id.toLowerCase() === q)
                      ?? guardsQ.data?.find((g) => g.employee_id.toLowerCase().startsWith(q));
                    if (match) setGuardId(match.id);
                  }}
                  placeholder="Search by employee ID number"
                  className="pl-9"
                  inputMode="search"
                />
              </div>
              <Select value={guardId} onValueChange={setGuardId}>
                <SelectTrigger><SelectValue placeholder="Select guard" /></SelectTrigger>
                <SelectContent>
                  {(guardsQ.data ?? [])
                    .filter((g) => {
                      const q = idSearch.trim().toLowerCase();
                      if (!q) return true;
                      return g.employee_id.toLowerCase().includes(q) || g.full_name.toLowerCase().includes(q);
                    })
                    .map((g) => (
                      <SelectItem key={g.id} value={g.id}>{g.full_name} ({g.employee_id})</SelectItem>
                    ))}
                </SelectContent>
              </Select>
            </div>
            </div>
          </div>

          {/* Category: Shift & Timing */}
          <div className="rounded-xl border border-border/60 bg-muted/20 p-4 space-y-3">
            <div className="flex items-center gap-2">
              <MapPin className="h-4 w-4 text-primary" />
              <h3 className="text-sm font-semibold">Shift &amp; Timing</h3>
            </div>
            <div className="grid md:grid-cols-2 gap-4">
            <div className="grid gap-2">
              <Label>Shift</Label>
              <Select value={shift} onValueChange={(v) => setShift(v as "A" | "B" | "C" | "custom")}>
                <SelectTrigger><SelectValue /></SelectTrigger>
                <SelectContent>
                  <SelectItem value="A">Shift A — 06:00 – 14:00</SelectItem>
                  <SelectItem value="B">Shift B — 14:00 – 22:00</SelectItem>
                  <SelectItem value="C">Shift C — 22:00 – 06:00</SelectItem>
                  <SelectItem value="custom">Custom</SelectItem>
                </SelectContent>
              </Select>
              <p className="text-xs text-muted-foreground">Auto-detected from current time: Shift {detectShiftFromNow()}</p>
            </div>
            <div className="grid gap-2">
              <Label>Check-in date</Label>
              <div className="flex items-center gap-2">
                <Input
                  type="date"
                  value={checkInAt}
                  onChange={(e) => setCheckInAt(e.target.value)}
                />
                <Button type="button" variant="outline" size="sm" onClick={() => setCheckInAt(todayIso())}>
                  Today
                </Button>
              </div>
              <p className="text-xs text-muted-foreground">Backdate a check-in by picking a custom date.</p>
            </div>
            </div>
          </div>

          {/* Category: Verification */}
          <div className="rounded-xl border border-border/60 bg-muted/20 p-4 space-y-3">
            <div className="flex items-center gap-2">
              <QrCode className="h-4 w-4 text-primary" />
              <h3 className="text-sm font-semibold">Verification</h3>
            </div>
            <Tabs value={method} onValueChange={(v) => setMethod(v as "qr" | "gps" | "face")}>
            <TabsList className="grid grid-cols-3 w-full max-w-md">
              <TabsTrigger value="gps"><MapPin className="h-4 w-4 mr-1" /> GPS</TabsTrigger>
              <TabsTrigger value="qr"><QrCode className="h-4 w-4 mr-1" /> QR Code</TabsTrigger>
              <TabsTrigger value="face"><ScanFace className="h-4 w-4 mr-1" /> Face</TabsTrigger>
            </TabsList>
            <TabsContent value="gps" className="pt-3">
              <div className="space-y-2">
                <div className="flex items-center gap-2 flex-wrap">
                  <Button type="button" size="sm" variant="outline" onClick={captureLocation} disabled={locating}>
                    <MapPin className="h-4 w-4 mr-1" />
                    {locating ? "Locating…" : coords ? "Re-capture GPS" : "Capture GPS"}
                  </Button>
                  {coords && (
                    <span className="text-xs font-mono text-muted-foreground">
                      {coords.lat.toFixed(5)}, {coords.lng.toFixed(5)} (±{Math.round(coords.accuracy ?? 0)}m)
                    </span>
                  )}
                </div>
                <p className="text-xs text-muted-foreground">
                  Coordinates are captured at check-in if not pre-locked here.
                </p>
              </div>
            </TabsContent>
            <TabsContent value="qr" className="pt-3 space-y-2">
              <div className="flex items-center justify-between">
                <Label>Scan post QR code</Label>
                {scanning ? (
                  <Button type="button" size="sm" variant="outline" onClick={stopScanner}>
                    <CameraOff className="h-4 w-4 mr-1" /> Stop camera
                  </Button>
                ) : (
                  <Button type="button" size="sm" variant="outline" onClick={startScanner}>
                    <Camera className="h-4 w-4 mr-1" /> Start camera
                  </Button>
                )}
              </div>
              <div
                id={scannerElId}
                className={`w-full max-w-sm rounded-md overflow-hidden border ${scanning ? "block" : "hidden"}`}
              />
              <Input
                value={qrCode}
                onChange={(e) => setQrCode(e.target.value)}
                onBlur={(e) => e.target.value && handleDecodedQr(e.target.value)}
                placeholder="Or paste QR token manually (GUARD:<empId> or POST:<name>)"
              />
              <p className="text-xs text-muted-foreground">
                Supported QR formats: <code>GUARD:&lt;employee_id&gt;</code> auto-selects guard,{" "}
                GPS coordinates are still captured when available.
              </p>
            </TabsContent>
            <TabsContent value="face" className="pt-3 space-y-3">
              <div className="flex items-center justify-between">
                <Label>Face verification</Label>
                {faceCamOn ? (
                  <Button type="button" size="sm" variant="outline" onClick={stopFaceCam}>
                    <CameraOff className="h-4 w-4 mr-1" /> Stop camera
                  </Button>
                ) : (
                  <Button type="button" size="sm" variant="outline" onClick={startFaceCam}>
                    <Camera className="h-4 w-4 mr-1" /> Start camera
                  </Button>
                )}
              </div>
              {faceCamOn && (
                <div className="space-y-2">
                  <video
                    ref={videoRef}
                    playsInline
                    muted
                    className="w-full max-w-xs aspect-square rounded-md border object-cover bg-black"
                  />
                  <Button type="button" size="sm" onClick={capturePhoto}>
                    <ScanFace className="h-4 w-4 mr-1" /> Capture
                  </Button>
                </div>
              )}
              {!faceCamOn && facePhoto && (
                <div className="space-y-2">
                  <img
                    src={facePhoto.url}
                    alt="Captured face"
                    className="w-full max-w-xs aspect-square rounded-md border object-cover"
                  />
                  <Button type="button" size="sm" variant="outline" onClick={() => { setFacePhoto(null); void startFaceCam(); }}>
                    <RefreshCw className="h-4 w-4 mr-1" /> Retake
                  </Button>
                </div>
              )}
              <p className="text-xs text-muted-foreground">
                Front camera photo is captured and stored securely as attendance proof. GPS coordinates are still recorded when available.
              </p>
            </TabsContent>
            </Tabs>
          </div>

          <div className="flex justify-end gap-2">
            {embedded ? (
              <Button type="button" variant="outline" onClick={() => onDone?.()}>
                Cancel
              </Button>
            ) : (
              <Button asChild variant="outline">
                <Link to="/attendance">Cancel</Link>
              </Button>
            )}
            <Button onClick={() => checkIn.mutate()} disabled={checkIn.isPending}>
              {checkIn.isPending ? "Checking in…" : "Check In"}
            </Button>
          </div>
        </CardContent>
      </Card>
    </div>
  );
}