'use client';

import { useEffect, useRef, useState } from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { AlertTriangle, Loader2, Upload, X } from 'lucide-react';

import { Button } from '@/components/ui/button';
import { Checkbox } from '@/components/ui/checkbox';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea';
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@/components/ui/select';
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetFooter } from '@/components/ui/sheet';
import { CurrencyInput } from '@/components/shared/currency-input';
import { CurrencySelect } from '@/components/shared/currency-select';
import { TripLabelCombobox } from '@/components/expenses/trip-label-combobox';
import { apiFetch } from '@/lib/api/client';
import type { z } from 'zod';
import { createExpenseSchema, type CreateExpenseInput } from '@/lib/validators/expenses';
import { EXPENSE_CATEGORIES, PAYMENT_METHODS, formatEnum } from '@/lib/constants';
import type { ExpenseRow } from './expense-columns';

interface UploadedReceipt {
  id: string;
  filename: string;
}

interface ExpenseFormDialogProps {
  open: boolean;
  onOpenChange: (open: boolean) => void;
  expense?: ExpenseRow;
}

export function ExpenseFormDialog({ open, onOpenChange, expense }: ExpenseFormDialogProps) {
  const queryClient = useQueryClient();
  const isEdit = !!expense;
  const fileInputRef = useRef<HTMLInputElement>(null);
  const [uploadedReceipt, setUploadedReceipt] = useState<UploadedReceipt | null>(null);
  const [previewUrl, setPreviewUrl] = useState<string | null>(null);
  const [noReceipt, setNoReceipt] = useState(false);
  const [uploadError, setUploadError] = useState<string | null>(null);
  const [isUploading, setIsUploading] = useState(false);

  const {
    register,
    handleSubmit,
    setValue,
    reset,
    watch,
    formState: { errors, isSubmitting },
  } = useForm<z.input<typeof createExpenseSchema>, unknown, CreateExpenseInput>({
    resolver: zodResolver(createExpenseSchema),
    defaultValues: {
      currency: 'USD',
      paymentStatus: 'unpaid',
    },
  });

  useEffect(() => {
    if (open && expense) {
      reset({
        establishmentName: expense.establishmentName ?? undefined,
        amount: Number(expense.amount),
        currency: expense.currency,
        category: expense.category as CreateExpenseInput['category'],
        paymentMethod: expense.paymentMethod as CreateExpenseInput['paymentMethod'],
        payer: expense.payer ?? undefined,
        expenseDate: new Date(expense.expenseDate),
        paymentStatus: (expense.paymentStatus as CreateExpenseInput['paymentStatus']) ?? 'unpaid',
        tripLabel: expense.tripLabel ?? undefined,
      });
      setUploadedReceipt(null);
      setPreviewUrl(null);
      setNoReceipt(Boolean(expense.noReceiptAcknowledged));
      setUploadError(null);
    } else if (open && !expense) {
      reset({
        currency: 'USD',
        paymentStatus: 'unpaid',
        expenseDate: new Date(),
      });
      setUploadedReceipt(null);
      setPreviewUrl(null);
      setNoReceipt(false);
      setUploadError(null);
    }
  }, [open, expense, reset]);

  // Capture the URL inside the effect closure so the cleanup revokes the
  // URL it observed at mount, not the one captured by a later render.
  // Audit caught a bug where the cleanup ran on every change and revoked
  // the URL that was still being shown.
  useEffect(() => {
    const url = previewUrl;
    return () => {
      if (url) URL.revokeObjectURL(url);
    };
  }, [previewUrl]);

  // Reset upload state whenever the sheet closes — re-opening on the same
  // expense was carrying stale state from the prior session.
  useEffect(() => {
    if (!open) {
      setUploadedReceipt(null);
      setPreviewUrl(null);
      setNoReceipt(false);
      setUploadError(null);
      setIsUploading(false);
      if (fileInputRef.current) fileInputRef.current.value = '';
    }
  }, [open]);

  const mutation = useMutation({
    mutationFn: (data: CreateExpenseInput) => {
      if (isEdit) {
        return apiFetch(`/api/v1/expenses/${expense.id}`, {
          method: 'PATCH',
          body: data,
        });
      }
      return apiFetch('/api/v1/expenses', { method: 'POST', body: data });
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['expenses'] });
      onOpenChange(false);
    },
  });

  async function handleFileChange(e: React.ChangeEvent<HTMLInputElement>) {
    const file = e.target.files?.[0];
    if (!file) return;
    setUploadError(null);
    if (previewUrl) URL.revokeObjectURL(previewUrl);
    setPreviewUrl(URL.createObjectURL(file));
    setIsUploading(true);
    try {
      const formData = new FormData();
      formData.append('file', file);
      formData.append('category', 'receipt');
      const res = await fetch('/api/v1/files/upload', {
        method: 'POST',
        body: formData,
        credentials: 'include',
      });
      if (!res.ok) throw new Error('Upload failed');
      const json = (await res.json()) as { data: { id: string; filename: string } };
      setUploadedReceipt({ id: json.data.id, filename: json.data.filename });
      setNoReceipt(false);
    } catch (err) {
      setUploadError(err instanceof Error ? err.message : 'Upload failed');
      setUploadedReceipt(null);
    } finally {
      setIsUploading(false);
    }
  }

  function clearReceipt() {
    if (previewUrl) URL.revokeObjectURL(previewUrl);
    setPreviewUrl(null);
    setUploadedReceipt(null);
    setUploadError(null);
    if (fileInputRef.current) fileInputRef.current.value = '';
  }

  function onSubmit(data: CreateExpenseInput) {
    mutation.mutate({
      ...data,
      receiptFileIds: uploadedReceipt ? [uploadedReceipt.id] : undefined,
      noReceiptAcknowledged: Boolean(noReceipt && !uploadedReceipt),
    });
  }

  const canSubmit = isEdit || Boolean(uploadedReceipt) || noReceipt;

  return (
    <Sheet open={open} onOpenChange={onOpenChange}>
      <SheetContent className="w-full sm:max-w-lg overflow-y-auto">
        <SheetHeader>
          <SheetTitle>{isEdit ? 'Edit Expense' : 'New Expense'}</SheetTitle>
        </SheetHeader>

        <form onSubmit={handleSubmit(onSubmit)} className="space-y-4 mt-4">
          <div className="space-y-2">
            <Label htmlFor="expenseDate">Date *</Label>
            <Input
              id="expenseDate"
              type="date"
              {...register('expenseDate', {
                setValueAs: (v) => (v ? new Date(v) : undefined),
              })}
              defaultValue={
                expense?.expenseDate
                  ? new Date(expense.expenseDate).toISOString().split('T')[0]
                  : new Date().toISOString().split('T')[0]
              }
            />
            {errors.expenseDate && (
              <p className="text-xs text-destructive">{errors.expenseDate.message}</p>
            )}
          </div>

          <div className="grid grid-cols-2 gap-3">
            <div className="space-y-2">
              <Label htmlFor="amount">Amount *</Label>
              <CurrencyInput
                id="amount"
                value={(watch('amount') as number | null | undefined) ?? ''}
                currency={watch('currency') ?? 'USD'}
                onChange={(v) =>
                  setValue('amount', v ?? Number.NaN, { shouldDirty: true, shouldValidate: true })
                }
              />
              {errors.amount && <p className="text-xs text-destructive">{errors.amount.message}</p>}
            </div>

            <div className="space-y-2">
              <Label htmlFor="currency">Currency</Label>
              <CurrencySelect
                id="currency"
                value={watch('currency') ?? 'USD'}
                onValueChange={(v) => setValue('currency', v, { shouldDirty: true })}
              />
              {errors.currency && (
                <p className="text-xs text-destructive">{errors.currency.message}</p>
              )}
            </div>
          </div>

          <div className="space-y-2">
            <Label htmlFor="establishmentName">Establishment</Label>
            <Input
              id="establishmentName"
              placeholder="Establishment name"
              {...register('establishmentName')}
            />
          </div>

          <div className="space-y-2">
            <Label htmlFor="category">Category</Label>
            <Select
              onValueChange={(v) => setValue('category', v as CreateExpenseInput['category'])}
              defaultValue={expense?.category ?? undefined}
            >
              <SelectTrigger id="category">
                <SelectValue placeholder="Select category" />
              </SelectTrigger>
              <SelectContent>
                {EXPENSE_CATEGORIES.map((cat) => (
                  <SelectItem key={cat} value={cat}>
                    {formatEnum(cat)}
                  </SelectItem>
                ))}
              </SelectContent>
            </Select>
          </div>

          <div className="space-y-2">
            <Label htmlFor="paymentMethod">Payment Method</Label>
            <Select
              onValueChange={(v) =>
                setValue('paymentMethod', v as CreateExpenseInput['paymentMethod'])
              }
              defaultValue={expense?.paymentMethod ?? undefined}
            >
              <SelectTrigger id="paymentMethod">
                <SelectValue placeholder="Select method" />
              </SelectTrigger>
              <SelectContent>
                {PAYMENT_METHODS.map((m) => (
                  <SelectItem key={m} value={m}>
                    {formatEnum(m)}
                  </SelectItem>
                ))}
              </SelectContent>
            </Select>
          </div>

          <div className="space-y-2">
            <Label htmlFor="payer">Payer</Label>
            <Input id="payer" placeholder="Who paid?" {...register('payer')} />
          </div>

          <div className="space-y-2">
            <Label htmlFor="paymentStatus">Payment Status</Label>
            <Select
              onValueChange={(v) => setValue('paymentStatus', v as 'unpaid' | 'paid' | 'partial')}
              defaultValue={expense?.paymentStatus ?? 'unpaid'}
            >
              <SelectTrigger id="paymentStatus">
                <SelectValue />
              </SelectTrigger>
              <SelectContent>
                <SelectItem value="unpaid">Unpaid</SelectItem>
                <SelectItem value="paid">Paid</SelectItem>
                <SelectItem value="partial">Partial</SelectItem>
              </SelectContent>
            </Select>
          </div>

          <div className="space-y-2">
            <Label htmlFor="tripLabel">Trip / event</Label>
            <TripLabelCombobox
              id="tripLabel"
              value={watch('tripLabel') ?? ''}
              onChange={(label) => setValue('tripLabel', label ?? undefined, { shouldDirty: true })}
              placeholder="e.g. Palm Beach 2026 (optional)"
            />
            <p className="text-xs text-muted-foreground">
              Group expenses by yacht show or business trip. Pick a past trip or type a new one.
            </p>
          </div>

          <div className="space-y-2">
            <Label htmlFor="description">Description</Label>
            <Textarea
              id="description"
              placeholder="Additional notes..."
              rows={3}
              {...register('description')}
            />
          </div>

          {!isEdit && (
            <div className="space-y-2 rounded-md border p-3">
              <Label className="text-sm font-medium">Receipt</Label>
              {previewUrl ? (
                <div className="relative">
                  <img
                    src={previewUrl}
                    alt="Receipt preview"
                    className="max-h-48 rounded border object-contain"
                  />
                  <button
                    type="button"
                    onClick={clearReceipt}
                    aria-label="Remove receipt"
                    className="absolute top-1 right-1 rounded-full bg-background/90 hover:bg-background border p-1 shadow-sm"
                  >
                    <X className="h-3 w-3" />
                  </button>
                  <p className="mt-1 text-xs text-muted-foreground">
                    {isUploading
                      ? 'Uploading...'
                      : uploadedReceipt
                        ? `Uploaded: ${uploadedReceipt.filename}`
                        : 'Selecting...'}
                  </p>
                </div>
              ) : (
                <Button
                  type="button"
                  variant="outline"
                  size="sm"
                  className="w-full"
                  disabled={noReceipt}
                  onClick={() => fileInputRef.current?.click()}
                >
                  <Upload className="mr-2 h-4 w-4" aria-hidden />
                  Upload receipt image or PDF
                </Button>
              )}
              <input
                ref={fileInputRef}
                type="file"
                accept="image/*,application/pdf"
                className="hidden"
                onChange={handleFileChange}
              />
              {uploadError && <p className="text-xs text-destructive">{uploadError}</p>}

              <div className="flex items-start gap-2 pt-1">
                <Checkbox
                  id="noReceipt"
                  checked={noReceipt}
                  onCheckedChange={(checked) => {
                    const next = checked === true;
                    setNoReceipt(next);
                    if (next) clearReceipt();
                  }}
                />
                <Label htmlFor="noReceipt" className="text-sm font-normal leading-tight">
                  I have no receipt for this expense
                </Label>
              </div>

              {noReceipt && (
                <div className="flex gap-2 rounded-md border border-amber-300 bg-amber-50 p-2 text-xs text-amber-900 dark:border-amber-900 dark:bg-amber-950/40 dark:text-amber-200">
                  <AlertTriangle className="h-4 w-4 shrink-0" aria-hidden />
                  <span>
                    Expenses without a receipt may not be reimbursed by the parent company. The PDF
                    export will flag this expense.
                  </span>
                </div>
              )}
            </div>
          )}

          {mutation.isError && (
            <p className="text-sm text-destructive">{(mutation.error as Error).message}</p>
          )}

          <SheetFooter className="pt-2">
            <Button type="button" variant="outline" onClick={() => onOpenChange(false)}>
              Cancel
            </Button>
            <Button
              type="submit"
              disabled={isSubmitting || mutation.isPending || isUploading || !canSubmit}
            >
              {(isSubmitting || mutation.isPending) && (
                <Loader2 className="mr-2 h-4 w-4 animate-spin" aria-hidden />
              )}
              {isEdit ? 'Save changes' : 'Create Expense'}
            </Button>
          </SheetFooter>
        </form>
      </SheetContent>
    </Sheet>
  );
}
