import { useState } from "react";
import { useQuery, useMutation } from "@tanstack/react-query";
import {
  Dialog,
  DialogContent,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Trash2, Plus, Save, X } from "lucide-react";
import { apiRequest, queryClient } from "@/lib/queryClient";
import { useToast } from "@/hooks/use-toast";
import { PROCEDURE_COMBOS } from "@/utils/procedureCombos";
import {
  CODE_MAP,
  getPriceForCodeWithAgeFromMap,
} from "@/utils/procedureCombosMapping";
import { Patient, AppointmentProcedure } from "@repo/db/types";
import { useLocation } from "wouter";
import { DeleteConfirmationDialog } from "../ui/deleteDialog";
import {
  DirectComboButtons,
  RegularComboButtons,
} from "@/components/procedure/procedure-combo-buttons";
import { AppointmentProcedureNotes } from "./appointment-procedure-notes";

interface Props {
  open: boolean;
  onOpenChange: (open: boolean) => void;
  appointmentId: number;
  patientId: number;
  patient: Patient;
}

export function AppointmentProceduresDialog({
  open,
  onOpenChange,
  appointmentId,
  patientId,
  patient,
}: Props) {
  const { toast } = useToast();

  // -----------------------------
  // state for manual add
  // -----------------------------
  const [manualCode, setManualCode] = useState("");
  const [manualLabel, setManualLabel] = useState("");
  const [manualFee, setManualFee] = useState("");
  const [manualTooth, setManualTooth] = useState("");
  const [manualSurface, setManualSurface] = useState("");

  // -----------------------------
  // state for inline edit
  // -----------------------------
  const [editingId, setEditingId] = useState<number | null>(null);
  const [editRow, setEditRow] = useState<Partial<AppointmentProcedure>>({});
  const [clearAllOpen, setClearAllOpen] = useState(false);

  // for redirection to claim submission
  const [, setLocation] = useLocation();

  // -----------------------------
  // fetch procedures
  // -----------------------------
  const { data: procedures = [], isLoading } = useQuery<AppointmentProcedure[]>(
    {
      queryKey: ["appointment-procedures", appointmentId],
      queryFn: async () => {
        const res = await apiRequest(
          "GET",
          `/api/appointment-procedures/${appointmentId}`,
        );
        if (!res.ok) throw new Error("Failed to load procedures");
        return res.json();
      },
      enabled: open && !!appointmentId,
    },
  );

  // -----------------------------
  // mutations
  // -----------------------------
  const addManualMutation = useMutation({
    mutationFn: async () => {
      const payload = {
        appointmentId,
        patientId,
        procedureCode: manualCode,
        procedureLabel: manualLabel || null,
        fee: manualFee ? Number(manualFee) : null,
        toothNumber: manualTooth || null,
        toothSurface: manualSurface || null,
        source: "MANUAL",
      };

      const res = await apiRequest(
        "POST",
        "/api/appointment-procedures",
        payload,
      );
      if (!res.ok) throw new Error("Failed to add procedure");
      return res.json();
    },
    onSuccess: () => {
      toast({ title: "Procedure added" });
      setManualCode("");
      setManualLabel("");
      setManualFee("");
      setManualTooth("");
      setManualSurface("");
      queryClient.invalidateQueries({
        queryKey: ["appointment-procedures", appointmentId],
      });
    },
    onError: (err: any) => {
      toast({
        title: "Error",
        description: err.message ?? "Failed to add procedure",
        variant: "destructive",
      });
    },
  });

  const bulkAddMutation = useMutation({
    mutationFn: async (rows: any[]) => {
      const res = await apiRequest(
        "POST",
        "/api/appointment-procedures/bulk",
        rows,
      );
      if (!res.ok) throw new Error("Failed to add combo procedures");
      return res.json();
    },
    onSuccess: () => {
      toast({ title: "Combo added" });
      queryClient.invalidateQueries({
        queryKey: ["appointment-procedures", appointmentId],
      });
    },
  });

  const deleteMutation = useMutation({
    mutationFn: async (id: number) => {
      const res = await apiRequest(
        "DELETE",
        `/api/appointment-procedures/${id}`,
      );
      if (!res.ok) throw new Error("Failed to delete");
    },
    onSuccess: () => {
      toast({ title: "Deleted" });
      queryClient.invalidateQueries({
        queryKey: ["appointment-procedures", appointmentId],
      });
    },
  });

  const clearAllMutation = useMutation({
    mutationFn: async () => {
      const res = await apiRequest(
        "DELETE",
        `/api/appointment-procedures/clear/${appointmentId}`,
      );
      if (!res.ok) throw new Error("Failed to clear procedures");
    },
    onSuccess: () => {
      toast({ title: "All procedures cleared" });
      queryClient.invalidateQueries({
        queryKey: ["appointment-procedures", appointmentId],
      });
      setClearAllOpen(false);
    },
    onError: (err: any) => {
      toast({
        title: "Error",
        description: err.message ?? "Failed to clear procedures",
        variant: "destructive",
      });
    },
  });

  const updateMutation = useMutation({
    mutationFn: async () => {
      if (!editingId) return;
      const res = await apiRequest(
        "PUT",
        `/api/appointment-procedures/${editingId}`,
        editRow,
      );
      if (!res.ok) throw new Error("Failed to update");
      return res.json();
    },
    onSuccess: () => {
      toast({ title: "Updated" });
      setEditingId(null);
      setEditRow({});
      queryClient.invalidateQueries({
        queryKey: ["appointment-procedures", appointmentId],
      });
    },
  });

  // -----------------------------
  // handlers
  // -----------------------------
  const handleAddCombo = (comboKey: string) => {
    const combo = PROCEDURE_COMBOS[comboKey];
    if (!combo || !patient?.dateOfBirth) return;

    const serviceDate = new Date();
    const dob = patient.dateOfBirth;

    const age = (() => {
      const birth = new Date(dob);
      const ref = new Date(serviceDate);
      let a = ref.getFullYear() - birth.getFullYear();
      const hadBirthday =
        ref.getMonth() > birth.getMonth() ||
        (ref.getMonth() === birth.getMonth() &&
          ref.getDate() >= birth.getDate());
      if (!hadBirthday) a -= 1;
      return a;
    })();

    const rows = combo.codes.map((code: string, idx: number) => {
      const priceDecimal = getPriceForCodeWithAgeFromMap(CODE_MAP, code, age);

      return {
        appointmentId,
        patientId,
        procedureCode: code,
        procedureLabel: combo.label,
        fee: priceDecimal.toNumber(),
        source: "COMBO",
        comboKey: comboKey,
        toothNumber: combo.toothNumbers?.[idx] ?? null,
      };
    });

    bulkAddMutation.mutate(rows);
  };

  const startEdit = (row: AppointmentProcedure) => {
    if (!row.id) return;

    setEditingId(row.id);
    setEditRow({
      procedureCode: row.procedureCode,
      procedureLabel: row.procedureLabel,
      fee: row.fee,
      toothNumber: row.toothNumber,
      toothSurface: row.toothSurface,
    });
  };

  const cancelEdit = () => {
    setEditingId(null);
    setEditRow({});
  };

  const handleDirectClaim = () => {
    setLocation(`/claims?appointmentId=${appointmentId}&mode=direct`);
    onOpenChange(false);
  };

  const handleManualClaim = () => {
    setLocation(`/claims?appointmentId=${appointmentId}&mode=manual`);
    onOpenChange(false);
  };

  // -----------------------------
  // UI
  // -----------------------------
  return (
    <Dialog open={open} onOpenChange={onOpenChange}>
      <DialogContent
        className="max-w-6xl max-h-[90vh] overflow-y-auto pointer-events-none"
        onPointerDownOutside={(e) => {
          if (clearAllOpen) {
            e.preventDefault(); // block only when delete dialog is open
          }
        }}
        onInteractOutside={(e) => {
          if (clearAllOpen) {
            e.preventDefault(); // block only when delete dialog is open
          }
        }}
      >
        <DialogHeader>
          <DialogTitle className="text-xl font-semibold">
            Appointment Procedures
          </DialogTitle>
        </DialogHeader>

        <AppointmentProcedureNotes
          appointmentId={appointmentId}
          enabled={open}
        />

        {/* ================= COMBOS ================= */}
        <div className="space-y-8 pointer-events-auto">
          <DirectComboButtons
            onDirectCombo={(comboKey) => {
              handleAddCombo(comboKey);
            }}
          />

          <RegularComboButtons
            onRegularCombo={(comboKey) => {
              handleAddCombo(comboKey);
            }}
          />
        </div>

        {/* ================= MANUAL ADD ================= */}
        <div className="mt-8 border rounded-lg p-4 bg-muted/20 space-y-3">
          <div className="font-medium text-sm">Add Manual Procedure</div>

          <div className="grid grid-cols-1 md:grid-cols-5 gap-3">
            <div>
              <Label>Code</Label>
              <Input
                value={manualCode}
                onChange={(e) => setManualCode(e.target.value)}
                placeholder="D0120"
              />
            </div>

            <div>
              <Label>Label</Label>
              <Input
                value={manualLabel}
                onChange={(e) => setManualLabel(e.target.value)}
                placeholder="Exam"
              />
            </div>

            <div>
              <Label>Fee</Label>
              <Input
                value={manualFee}
                onChange={(e) => setManualFee(e.target.value)}
                placeholder="100"
                type="number"
              />
            </div>

            <div>
              <Label>Tooth</Label>
              <Input
                value={manualTooth}
                onChange={(e) => setManualTooth(e.target.value)}
                placeholder="14"
              />
            </div>

            <div>
              <Label>Surface</Label>
              <Input
                value={manualSurface}
                onChange={(e) => setManualSurface(e.target.value)}
                placeholder="MO"
              />
            </div>
          </div>

          <div className="flex justify-end">
            <Button
              size="sm"
              onClick={() => addManualMutation.mutate()}
              disabled={!manualCode || addManualMutation.isPending}
            >
              <Plus className="h-4 w-4 mr-1" />
              Add Procedure
            </Button>
          </div>
        </div>

        {/* ================= LIST ================= */}
        <div className="mt-8 space-y-2">
          <div className="flex items-center justify-between">
            <div className="text-sm font-semibold">Selected Procedures</div>

            <Button
              variant="destructive"
              size="sm"
              disabled={!procedures.length}
              onClick={() => setClearAllOpen(true)}
            >
              Clear All
            </Button>
          </div>

          <div className="border rounded-lg divide-y bg-white">
            {/* ===== TABLE HEADER ===== */}
            <div className="grid grid-cols-[90px_1fr_90px_80px_80px_72px_72px] gap-2 px-3 py-2 text-xs font-semibold text-muted-foreground bg-muted/40">
              <div>Code</div>
              <div>Label</div>
              <div>Fee</div>
              <div>Tooth</div>
              <div>Surface</div>
              <div className="text-center">Edit</div>
              <div className="text-center">Delete</div>
            </div>

            {isLoading && (
              <div className="p-4 text-sm text-muted-foreground">
                Loading...
              </div>
            )}

            {!isLoading && procedures.length === 0 && (
              <div className="p-4 text-sm text-muted-foreground">
                No procedures added
              </div>
            )}

            {procedures.map((p) => (
              <div
                key={p.id}
                className="grid grid-cols-[90px_1fr_90px_80px_80px_72px_72px] gap-2 px-3 py-3 text-sm hover:bg-muted/40 transition"
              >
                {editingId === p.id ? (
                  <>
                    <Input
                      className="w-[90px]"
                      value={editRow.procedureCode ?? ""}
                      onChange={(e) =>
                        setEditRow({
                          ...editRow,
                          procedureCode: e.target.value,
                        })
                      }
                    />
                    <Input
                      className="flex-1"
                      value={editRow.procedureLabel ?? ""}
                      onChange={(e) =>
                        setEditRow({
                          ...editRow,
                          procedureLabel: e.target.value,
                        })
                      }
                    />
                    <Input
                      className="w-[90px]"
                      value={
                        editRow.fee !== undefined && editRow.fee !== null
                          ? String(editRow.fee)
                          : ""
                      }
                      onChange={(e) =>
                        setEditRow({ ...editRow, fee: Number(e.target.value) })
                      }
                    />

                    <Input
                      className="w-[80px]"
                      value={editRow.toothNumber ?? ""}
                      onChange={(e) =>
                        setEditRow({
                          ...editRow,
                          toothNumber: e.target.value,
                        })
                      }
                    />
                    <Input
                      className="w-[80px]"
                      value={editRow.toothSurface ?? ""}
                      onChange={(e) =>
                        setEditRow({
                          ...editRow,
                          toothSurface: e.target.value,
                        })
                      }
                    />
                    <div className="flex justify-center">
                      <Button
                        size="icon"
                        variant="ghost"
                        onClick={() => updateMutation.mutate()}
                      >
                        <Save className="h-4 w-4" />
                      </Button>
                    </div>

                    <div className="flex justify-center">
                      <Button size="icon" variant="ghost" onClick={cancelEdit}>
                        <X className="h-4 w-4" />
                      </Button>
                    </div>
                  </>
                ) : (
                  <>
                    <div className="w-[90px] font-medium">
                      {p.procedureCode}
                    </div>
                    <div className="flex-1 text-muted-foreground">
                      {p.procedureLabel}
                    </div>
                    <div className="w-[90px]">
                      {p.fee !== null && p.fee !== undefined
                        ? String(p.fee)
                        : ""}
                    </div>

                    <div className="w-[80px]">{p.toothNumber}</div>
                    <div className="w-[80px]">{p.toothSurface}</div>

                    <div className="flex justify-center">
                      <Button
                        size="icon"
                        variant="ghost"
                        onClick={() => startEdit(p)}
                      >
                        Edit
                      </Button>
                    </div>

                    <div className="flex justify-center">
                      <Button
                        size="icon"
                        variant="ghost"
                        onClick={() => deleteMutation.mutate(p.id!)}
                      >
                        <Trash2 className="h-4 w-4 text-red-500" />
                      </Button>
                    </div>
                  </>
                )}
              </div>
            ))}
          </div>
        </div>

        {/* ================= FOOTER ================= */}
        <div className="flex justify-between items-center gap-2 mt-8 pt-4 border-t">
          <div className="flex gap-2">
            <Button
              className="bg-green-600 hover:bg-green-700"
              disabled={!procedures.length}
              onClick={handleDirectClaim}
            >
              Direct Claim
            </Button>

            <Button
              variant="outline"
              className="border-blue-500 text-blue-600 hover:bg-blue-50"
              disabled={!procedures.length}
              onClick={handleManualClaim}
            >
              Manual Claim
            </Button>
          </div>

          <Button variant="outline" onClick={() => onOpenChange(false)}>
            Close
          </Button>
        </div>
      </DialogContent>

      <DeleteConfirmationDialog
        isOpen={clearAllOpen}
        entityName="all procedures for this appointment"
        onCancel={() => setClearAllOpen(false)}
        onConfirm={() => {
          setClearAllOpen(false);
          clearAllMutation.mutate();
        }}
      />
    </Dialog>
  );
}
