import { useState, useEffect, useRef } from "react";
import { useQuery, useMutation } from "@tanstack/react-query";
import { Dialog, DialogContent, DialogHeader, DialogTitle, } from "@/components/ui/dialog";
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select";
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 { findPriceMismatches, } from "@/utils/procedureCombosMapping";
import { useLocation } from "wouter";
import { DeleteConfirmationDialog } from "../ui/deleteDialog";
import { DirectComboButtons, RegularComboButtons, } from "@/components/procedure/procedure-combo-buttons";
export function AppointmentProceduresDialog({ open, onOpenChange, appointmentId, patientId, patient, serviceDate, }) {
    const { toast } = useToast();
    const [, setLocation] = useLocation();
    // NPI provider state — stored per-appointment on the procedure rows
    const [selectedNpiProviderId, setSelectedNpiProviderId] = useState(null);
    const emptyRow = () => ({ code: "", label: "", fee: "", tooth: "", surface: "" });
    const [pendingRows, setPendingRows] = useState([emptyRow(), emptyRow(), emptyRow()]);
    // reset pending rows when dialog opens
    useEffect(() => {
        if (open)
            setPendingRows([emptyRow(), emptyRow(), emptyRow()]);
    }, [open]);
    // inline edit state
    const [editingId, setEditingId] = useState(null);
    const [editRow, setEditRow] = useState({});
    const [clearAllOpen, setClearAllOpen] = useState(false);
    // price mismatch dialog
    const [priceMismatches, setPriceMismatches] = useState([]);
    const pendingAction = useRef(null);
    const deriveInsuranceSiteKey = (provider) => {
        const p = (provider || "").toLowerCase().trim();
        if (!p)
            return "";
        if (p.includes("masshealth") || p === "mh" || p === "mass health")
            return "MH";
        if (p.includes("commonwealth care alliance") || p === "cca")
            return "CCA";
        if (p.includes("ddma") || p.includes("delta dental ma"))
            return "DDMA";
        if (p.includes("tufts") || p.includes("dentaquest") || p === "tuftssco")
            return "TuftsSCO";
        if ((p.includes("united") && p.includes("sco")) || p === "unitedsco")
            return "UnitedSCO";
        return "";
    };
    const runWithPriceCheck = (procedureCode, fee, action) => {
        const siteKey = deriveInsuranceSiteKey(patient?.insuranceProvider);
        if (!siteKey || !procedureCode.trim() || !fee) {
            action();
            return;
        }
        const mismatches = findPriceMismatches([{ procedureCode, totalBilled: fee, procedureDate: "" }], siteKey, patient?.dateOfBirth || "", serviceDate ?? new Date().toISOString().slice(0, 10));
        if (mismatches.length === 0) {
            action();
        }
        else {
            pendingAction.current = action;
            setPriceMismatches(mismatches);
        }
    };
    const savePricesToSchedule = async (mismatches) => {
        const siteKey = deriveInsuranceSiteKey(patient?.insuranceProvider);
        await Promise.all(mismatches.map(m => apiRequest("POST", "/api/fee-schedule/update-price", {
            siteKey,
            procedureCode: m.procedureCode,
            price: m.enteredPrice,
        })));
    };
    // ── NPI Providers ──────────────────────────────────────────────
    const { data: npiProviders = [] } = useQuery({
        queryKey: ["/api/npiProviders/"],
        queryFn: async () => {
            const res = await apiRequest("GET", "/api/npiProviders/");
            if (!res.ok)
                throw new Error("Failed to fetch NPI providers");
            return res.json();
        },
        enabled: open,
    });
    // ── Procedures ─────────────────────────────────────────────────
    const { data: procedures = [], isLoading } = useQuery({
        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,
    });
    // Sync NPI provider from saved procedures when they load
    useEffect(() => {
        if (!procedures.length)
            return;
        const saved = procedures[0]?.npiProviderId ?? null;
        if (saved != null)
            setSelectedNpiProviderId(Number(saved));
    }, [procedures]);
    // Default NPI provider to Mary Scannell / first when none saved yet
    useEffect(() => {
        if (selectedNpiProviderId != null || !npiProviders.length)
            return;
        const mary = npiProviders.find((p) => p.providerName.toLowerCase() === "mary scannell");
        setSelectedNpiProviderId((mary ?? npiProviders[0])?.id ?? null);
    }, [npiProviders, selectedNpiProviderId]);
    // ── Mutations ──────────────────────────────────────────────────
    const setNpiMutation = useMutation({
        mutationFn: async (npiProviderId) => {
            const res = await apiRequest("PUT", `/api/appointment-procedures/set-npi-provider/${appointmentId}`, { npiProviderId });
            if (!res.ok)
                throw new Error("Failed to update provider");
        },
        onSuccess: () => {
            toast({ title: "Rendering provider saved" });
            queryClient.invalidateQueries({ queryKey: ["appointment-procedures", appointmentId] });
        },
        onError: (err) => {
            toast({ title: "Error", description: err.message, variant: "destructive" });
        },
    });
    const bulkAddMutation = useMutation({
        mutationFn: async (rows) => {
            const res = await apiRequest("POST", "/api/appointment-procedures/bulk", rows);
            if (!res.ok)
                throw new Error("Failed to add procedures");
            return res.json();
        },
        onSuccess: () => {
            toast({ title: "Procedures saved" });
            setPendingRows([emptyRow(), emptyRow(), emptyRow()]);
            queryClient.invalidateQueries({ queryKey: ["appointment-procedures", appointmentId] });
        },
    });
    const deleteMutation = useMutation({
        mutationFn: async (id) => {
            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) => {
            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) => {
        const combo = PROCEDURE_COMBOS[comboKey];
        if (!combo)
            return;
        const rows = combo.codes.map((code, idx) => ({
            appointmentId,
            patientId,
            npiProviderId: selectedNpiProviderId ?? null,
            procedureCode: code,
            procedureLabel: combo.label,
            fee: 0,
            source: "COMBO",
            comboKey,
            toothNumber: combo.toothNumbers?.[idx] ?? null,
        }));
        bulkAddMutation.mutate(rows);
    };
    const startEdit = (row) => {
        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 handleSavePendingRows = () => {
        const rows = pendingRows
            .filter((r) => r.code.trim())
            .map((r) => ({
            appointmentId,
            patientId,
            npiProviderId: selectedNpiProviderId ?? null,
            procedureCode: r.code.trim().toUpperCase(),
            procedureLabel: r.label || null,
            fee: r.fee ? Number(r.fee) : 0,
            toothNumber: r.tooth || null,
            toothSurface: r.surface || null,
            source: "MANUAL",
        }));
        if (!rows.length)
            return;
        bulkAddMutation.mutate(rows);
    };
    const handleDirectClaim = () => {
        setLocation(`/claims?appointmentId=${appointmentId}&mode=direct`);
        onOpenChange(false);
    };
    const handleManualClaim = () => {
        setLocation(`/claims?appointmentId=${appointmentId}&mode=manual`);
        onOpenChange(false);
    };
    const selectedProvider = npiProviders.find((p) => p.id === selectedNpiProviderId);
    // ── 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(); }} onInteractOutside={(e) => { if (clearAllOpen)
        e.preventDefault(); }}>
        <DialogHeader>
          <DialogTitle className="text-xl font-semibold">
            Appointment Procedures
            {serviceDate && <span className="ml-3 text-base font-normal text-muted-foreground">{serviceDate}</span>}
          </DialogTitle>
        </DialogHeader>

        {/* ── Rendering Provider ─────────────────────────────── */}
        <div className="flex items-end gap-3 p-3 bg-blue-50 border border-blue-200 rounded-lg">
          <div className="flex-1">
            <Label className="text-sm font-medium text-blue-800">Rendering Provider (NPI)</Label>
            <Select value={selectedNpiProviderId?.toString() ?? ""} onValueChange={(v) => setSelectedNpiProviderId(v ? Number(v) : null)}>
              <SelectTrigger className="mt-1 bg-white">
                <SelectValue placeholder="Select NPI Provider"/>
              </SelectTrigger>
              <SelectContent>
                {npiProviders.map((p) => (<SelectItem key={p.id} value={String(p.id)}>
                    {p.npiNumber} — {p.providerName}
                  </SelectItem>))}
              </SelectContent>
            </Select>
          </div>
          <Button size="sm" className="mb-0.5" onClick={() => setNpiMutation.mutate(selectedNpiProviderId)} disabled={setNpiMutation.isPending || !procedures.length}>
            <Save className="h-4 w-4 mr-1"/>
            Set for All
          </Button>
          {selectedProvider && (<span className="text-sm text-blue-700 mb-1 whitespace-nowrap">
              ✓ {selectedProvider.providerName}
            </span>)}
        </div>

        {/* ── Combos ─────────────────────────────────────────── */}
        <div className="space-y-8 pointer-events-auto">
          <DirectComboButtons onDirectCombo={handleAddCombo}/>
          <RegularComboButtons onRegularCombo={handleAddCombo}/>
        </div>

        {/* ── Pending Lines ───────────────────────────────────── */}
        <div className="mt-8 border rounded-lg p-4 bg-muted/20 space-y-2">
          <div className="font-medium text-sm mb-3">Add Procedures</div>
          {/* Column headers */}
          <div className="grid grid-cols-[100px_1fr_90px_80px_80px_36px] gap-2 px-1 text-xs font-semibold text-muted-foreground">
            <div>Code</div><div>Label</div><div>Fee</div><div>Tooth</div><div>Surface</div><div />
          </div>
          {pendingRows.map((row, i) => (<div key={i} className="grid grid-cols-[100px_1fr_90px_80px_80px_36px] gap-2 items-center">
              <Input placeholder="D0120" value={row.code} onChange={(e) => setPendingRows((prev) => prev.map((r, idx) => idx === i ? { ...r, code: e.target.value } : r))}/>
              <Input placeholder="Exam" value={row.label} onChange={(e) => setPendingRows((prev) => prev.map((r, idx) => idx === i ? { ...r, label: e.target.value } : r))}/>
              <Input type="number" placeholder="0.00" value={row.fee} onChange={(e) => setPendingRows((prev) => prev.map((r, idx) => idx === i ? { ...r, fee: e.target.value } : r))}/>
              <Input placeholder="14" value={row.tooth} onChange={(e) => setPendingRows((prev) => prev.map((r, idx) => idx === i ? { ...r, tooth: e.target.value } : r))}/>
              <Input placeholder="MO" value={row.surface} onChange={(e) => setPendingRows((prev) => prev.map((r, idx) => idx === i ? { ...r, surface: e.target.value } : r))}/>
              <button type="button" className="p-1 rounded hover:bg-red-50" onClick={() => setPendingRows((prev) => prev.filter((_, idx) => idx !== i))}>
                <Trash2 className="h-4 w-4 text-red-400 hover:text-red-600"/>
              </button>
            </div>))}
          <div className="flex justify-between items-center pt-2">
            <Button size="sm" variant="outline" onClick={() => setPendingRows((prev) => [...prev, emptyRow()])}>
              <Plus className="h-4 w-4 mr-1"/>
              Add Line
            </Button>
            <Button size="sm" onClick={handleSavePendingRows} disabled={!pendingRows.some((r) => r.code.trim()) || bulkAddMutation.isPending}>
              <Save className="h-4 w-4 mr-1"/>
              Save Lines
            </Button>
          </div>
        </div>

        {/* ── Procedures List ─────────────────────────────────── */}
        <div className="mt-8 space-y-2">
          <div className="flex items-center justify-between">
            <div className="text-sm font-semibold">Saved Procedures ({procedures.length})</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">
            <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 yet</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={() => runWithPriceCheck(editRow.procedureCode || "", Number(editRow.fee), () => 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(); }}/>

      {/* Price mismatch dialog */}
      <AlertDialog open={priceMismatches.length > 0} onOpenChange={open => { if (!open)
        setPriceMismatches([]); }}>
        <AlertDialogContent>
          <AlertDialogHeader>
            <AlertDialogTitle>Save new price to the app?</AlertDialogTitle>
            <AlertDialogDescription asChild>
              <div className="space-y-2">
                <p>The following procedure prices differ from the fee schedule:</p>
                <ul className="text-sm space-y-1">
                  {priceMismatches.map(m => (<li key={m.procedureCode} className="flex justify-between gap-4">
                      <span className="font-medium">{m.procedureCode}</span>
                      <span className="text-muted-foreground">Schedule: ${m.schedulePrice.toFixed(2)}</span>
                      <span className="text-foreground font-semibold">Entered: ${m.enteredPrice.toFixed(2)}</span>
                    </li>))}
                </ul>
                <p className="text-sm">Do you want to save the new price(s) to the fee schedule for future use?</p>
              </div>
            </AlertDialogDescription>
          </AlertDialogHeader>
          <AlertDialogFooter>
            <AlertDialogCancel onClick={() => {
            setPriceMismatches([]);
            pendingAction.current?.();
            pendingAction.current = null;
        }}>
              No
            </AlertDialogCancel>
            <AlertDialogAction onClick={async () => {
            await savePricesToSchedule(priceMismatches);
            setPriceMismatches([]);
            pendingAction.current?.();
            pendingAction.current = null;
        }}>
              Yes
            </AlertDialogAction>
          </AlertDialogFooter>
        </AlertDialogContent>
      </AlertDialog>
    </Dialog>);
}
