import { useEffect, useState } from 'react';
import { Alert, Button, Group, Modal, MultiSelect, NumberInput, SegmentedControl, Select, SimpleGrid, Stack, Text, TextInput } from '@mantine/core';
import { Info, Scale } from 'lucide-react';

import { useMutation, useQuery } from '@tanstack/react-query';

import { api } from '@/services/api';
import { useToast } from '@/hooks/use-toast';
import { warehouseService } from '@/services/warehouse.service';
import type { WarehouseInventoryItem } from '@/types/warehouse';
import { extractErrorMessage } from './options';
import { openPdfBlob } from './pdf';

interface ReleaseOrderModalProps {
  opened: boolean;
  onClose: () => void;
  item: WarehouseInventoryItem | null;
  truckPrefill?: ReleaseOrderTruckPrefill | null;
}

export interface ReleaseOrderTruckPrefill {
  truckPlateNumber?: string | null;
  trailerPlateNumber?: string | null;
  driverName?: string | null;
  driverLicense?: string | null;
  driverPhone?: string | null;
  truckType?: string | null;
  containerNumber?: string | null;
}

const toIsoDateTime = (value: string) => {
  if (!value) return undefined;
  const date = new Date(value);
  return Number.isNaN(date.getTime()) ? undefined : date.toISOString();
};

const toLocalDateTimeInput = (value?: string | null) => {
  if (!value) return '';
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) return '';
  const offsetMs = date.getTimezoneOffset() * 60_000;
  return new Date(date.getTime() - offsetMs).toISOString().slice(0, 16);
};

const generateReleaseReference = (item: WarehouseInventoryItem | null) => {
  const bookingReference = item?.booking?.reference;
  if (bookingReference) return `REL-${bookingReference.replace(/^BK-?/i, '')}`;
  if (item?.bookingId) return `REL-${item.bookingId.replace(/-/g, '').slice(0, 8).toUpperCase()}`;
  return '';
};

const lineValue = (notes: string | null | undefined, label: string) => {
  const match = notes?.match(new RegExp(`^${label}:\\s*(.+)$`, 'im'));
  return match?.[1]?.trim() ?? '';
};

const lineNumber = (notes: string | null | undefined, label: string): number | '' => {
  const value = lineValue(notes, label).replace(/\s*(kg|t)$/i, '');
  if (!value) return '';
  const parsed = Number(value);
  return Number.isFinite(parsed) ? parsed : '';
};

const splitContainerNumbers = (value: string | null | undefined) =>
  (value ?? '')
    .split(/[,;\n]+/)
    .map((number) => number.trim())
    .filter(Boolean);

const getItemContainerNumber = (item: WarehouseInventoryItem | null) =>
  (item as (WarehouseInventoryItem & { containerNumber?: string | null }) | null)?.containerNumber ?? '';

const assignedTruckValue = (item: WarehouseInventoryItem | null, key: keyof NonNullable<WarehouseInventoryItem['booking']>) =>
  item?.booking?.[key] == null ? '' : String(item.booking[key]);

const isContainerInventory = (item: WarehouseInventoryItem | null, containerCount: number) => {
  const freightType = (item as (WarehouseInventoryItem & { booking?: { freightType?: string | null } | null }) | null)
    ?.booking?.freightType;
  return Boolean(item?.containerId || containerCount > 0 || freightType === 'CONTAINER');
};

const initialContainerNumbers = (item: WarehouseInventoryItem | null, savedContainerNumber: string) => {
  const savedNumbers = splitContainerNumbers(savedContainerNumber);
  const itemNumbers = splitContainerNumbers(getItemContainerNumber(item));
  const sourceNumbers = savedNumbers.length ? savedNumbers : itemNumbers;
  const quantityCount = isContainerInventory(item, sourceNumbers.length) ? Number(item?.quantity ?? 0) : 0;
  const expectedCount = Math.max(1, sourceNumbers.length, quantityCount);
  return Array.from({ length: expectedCount }, (_, index) => sourceNumbers[index] ?? '');
};

const parseInspectionNote = (notes: string | null | undefined) => {
  const marker = '[Exit Inspection]';
  const index = notes?.lastIndexOf(marker) ?? -1;
  const note = index >= 0 ? notes?.slice(index + marker.length) : notes;
  return {
    truckPlateNumber: lineValue(note, 'Truck Plate'),
    trailerPlateNumber: lineValue(note, 'Trailer Plate'),
    driverName: lineValue(note, 'Driver'),
    driverLicense: lineValue(note, 'Driver License'),
    driverPhone: lineValue(note, 'Driver Phone'),
    truckType: lineValue(note, 'Truck Type'),
    containerNumber: lineValue(note, 'Container Number'),
    gateInTime: toLocalDateTimeInput(lineValue(note, 'Gate In Time')),
    tareWeight: lineNumber(note, 'Tare Weight'),
    grossWeight: lineNumber(note, 'Gross Weight'),
    netWeight: lineNumber(note, 'Net Weight'),
    gateOutTime: toLocalDateTimeInput(lineValue(note, 'Gate Out Time')),
    weighingSkipped: /^Weighing:\s*SKIPPED/im.test(note ?? ''),
  };
};

export function ReleaseOrderModal({ opened, onClose, item, truckPrefill }: ReleaseOrderModalProps) {
  const { toast } = useToast();
  const releaseMutation = useMutation(api.warehouses.release.mutationOptions());
  const bookingId = item?.booking?.id;
  // Customer self-haul trucks assigned to this booking via the portal.
  const { data: customerTrucks = [] } = useQuery({
    queryKey: ['release-customer-trucks', bookingId],
    queryFn: () => warehouseService.getCustomerTrucks(bookingId as string),
    enabled: opened && Boolean(bookingId),
  });
  // Per-container cargo weights — the truck's net (gross − tare) must equal the
  // total cargo weight of the containers selected as loaded on it.
  const { data: containerWeights = [] } = useQuery({
    queryKey: ['release-container-weights', bookingId],
    queryFn: () => warehouseService.getContainerWeights(bookingId as string),
    enabled: opened && Boolean(bookingId),
  });
  const [reference, setReference] = useState('');
  const [truckPlateNumber, setTruckPlateNumber] = useState('');
  const [trailerPlateNumber, setTrailerPlateNumber] = useState('');
  const [driverName, setDriverName] = useState('');
  const [driverLicense, setDriverLicense] = useState('');
  const [driverPhone, setDriverPhone] = useState('');
  const [truckType, setTruckType] = useState('');
  const [containerNumbers, setContainerNumbers] = useState<string[]>(['']);
  const [gateInTime, setGateInTime] = useState('');
  const [tareWeight, setTareWeight] = useState<number | ''>('');
  // Containers may skip the weighbridge (decided at arrival, sticks for exit). Bulk always weighs.
  const [weighTruck, setWeighTruck] = useState<'yes' | 'no'>('yes');
  const [grossWeight, setGrossWeight] = useState<number | ''>('');
  const [netWeight, setNetWeight] = useState<number | ''>('');
  const [gateOutTime, setGateOutTime] = useState('');
  const [downloading, setDownloading] = useState(false);

  useEffect(() => {
    if (opened) {
      const inspection = parseInspectionNote(item?.notes);
      const assignedTruckPlate = assignedTruckValue(item, 'customerTruckPlateNumber');
      const assignedDriverName = assignedTruckValue(item, 'customerTruckDriverName');
      const assignedTruckType = assignedTruckValue(item, 'customerTruckType');
      const assignedContainerNumber = assignedTruckValue(item, 'customerTruckContainerNumber');
      const prefillContainerNumber = truckPrefill?.containerNumber ?? '';
      setReference(item?.releaseOrderReference ?? generateReleaseReference(item));
      setTruckPlateNumber(inspection.truckPlateNumber || truckPrefill?.truckPlateNumber || assignedTruckPlate || '');
      setTrailerPlateNumber(inspection.trailerPlateNumber || truckPrefill?.trailerPlateNumber || '');
      setDriverName(inspection.driverName || truckPrefill?.driverName || assignedDriverName || '');
      setDriverLicense(inspection.driverLicense || truckPrefill?.driverLicense || '');
      setDriverPhone(inspection.driverPhone || truckPrefill?.driverPhone || '');
      setTruckType(inspection.truckType || truckPrefill?.truckType || assignedTruckType || '');
      setContainerNumbers(initialContainerNumbers(item, inspection.containerNumber || prefillContainerNumber || assignedContainerNumber));
      setGateInTime(inspection.gateInTime);
      setTareWeight(inspection.tareWeight);
      setWeighTruck(inspection.weighingSkipped ? 'no' : 'yes');
      setGrossWeight(inspection.grossWeight);
      setNetWeight(item?.weight == null ? inspection.netWeight : Number(item.weight));
      setGateOutTime(inspection.gateOutTime);
    }
  }, [opened, item, truckPrefill]);

  const savedInspection = parseInspectionNote(item?.notes);
  const isExitStep = savedInspection.tareWeight !== '' || savedInspection.weighingSkipped;
  const isEntranceLocked = isExitStep;
  const isCustomerAssignedTruck = Boolean(item?.booking?.customerTruckAssignedAt);
  const hasLastMileTruckPrefill = Boolean(truckPrefill?.truckPlateNumber || truckPrefill?.trailerPlateNumber);

  // Registered trucks for THIS booking, from both sources: EDR last-mile
  // (truckPrefill) and the customer portal (customer_truck_assignments).
  const assignedTruckOptions = [
    ...(truckPrefill?.truckPlateNumber
      ? [
          {
            value: truckPrefill.truckPlateNumber,
            label: `Last-mile · ${truckPrefill.truckPlateNumber}`,
            trailerPlate: truckPrefill.trailerPlateNumber ?? '',
            driverName: truckPrefill.driverName ?? '',
            driverPhone: truckPrefill.driverPhone ?? '',
            truckType: truckPrefill.truckType ?? '',
          },
        ]
      : []),
    ...customerTrucks.map((t) => ({
      value: t.plateNumber,
      label: `Customer · ${t.plateNumber} — ${t.driverName}`,
      trailerPlate: '',
      driverName: t.driverName,
      driverPhone: '',
      truckType: t.truckType,
    })),
  ];
  // Only trucks actually assigned to THIS booking (last-mile prefill or customer
  // portal) are selectable. No global fleet list — if nothing is assigned, the
  // operator types the plate manually in the field below.
  const truckSelectOptions = assignedTruckOptions;
  // Neither a last-mile truck nor a customer truck has been assigned yet.
  const noTruckAssigned = assignedTruckOptions.length === 0 && !isCustomerAssignedTruck;
  const isTruckIdentityLocked = isEntranceLocked || isCustomerAssignedTruck || hasLastMileTruckPrefill;
  const isDriverNameLocked = isEntranceLocked || isCustomerAssignedTruck || Boolean(truckPrefill?.driverName);

  // Which containers ride this truck, and their combined cargo weight. When the
  // booking has container weights, that sum is the authoritative net; the
  // operator selects the containers loaded on the truck at exit.
  const hasContainerWeights = containerWeights.length > 0;
  const containerWeightByNumber = new Map(
    containerWeights.map((c) => [c.containerNumber.toUpperCase(), Number(c.weightTons) || 0]),
  );
  const containerSelectData = containerWeights.map((c) => ({
    value: c.containerNumber,
    label: `${c.containerNumber} · ${(Number(c.weightTons) || 0).toLocaleString()} t`,
  }));
  const selectedContainerNumbers = containerNumbers.map((n) => n.trim()).filter(Boolean);
  const selectedCargoWeight = Number(
    selectedContainerNumbers
      .reduce((sum, n) => sum + (containerWeightByNumber.get(n.toUpperCase()) ?? 0), 0)
      .toFixed(3),
  );
  // Skip is only offered for container bookings; bulk always weighs.
  const skipWeighing = hasContainerWeights && weighTruck === 'no';
  const useContainerNet = hasContainerWeights && selectedContainerNumbers.length > 0 && !skipWeighing;

  const systemNetWeight = useContainerNet
    ? selectedCargoWeight
    : item?.weight == null
      ? netWeight
      : Number(item.weight);
  const computedNetWeight =
    tareWeight !== '' && grossWeight !== '' ? Number((Number(grossWeight) - Number(tareWeight)).toFixed(3)) : null;
  const weightMismatch =
    !skipWeighing &&
    computedNetWeight != null && systemNetWeight !== '' && Math.abs(Number(systemNetWeight) - computedNetWeight) > 0.001;
  const title = isExitStep ? 'Customer truck leaving and exit weighing' : 'Customer truck arrival weighing';

  const handleSubmit = async () => {
    if (!item) return;
    if (!truckPlateNumber.trim() || !driverName.trim()) {
      toast({ variant: 'destructive', title: 'Truck plate and driver name are required' });
      return;
    }
    if (!gateInTime || (!skipWeighing && tareWeight === '')) {
      toast({
        variant: 'destructive',
        title: skipWeighing ? 'Gate in time is required' : 'Gate in time and tare weight are required',
      });
      return;
    }
    if (isExitStep && (!gateOutTime || (!skipWeighing && grossWeight === ''))) {
      toast({
        variant: 'destructive',
        title: skipWeighing ? 'Gate out time is required' : 'Gate out time and gross weight are required',
      });
      return;
    }
    if (isExitStep && hasContainerWeights && selectedContainerNumbers.length === 0) {
      toast({ variant: 'destructive', title: 'Select the containers loaded on this truck' });
      return;
    }
    if (isExitStep && !skipWeighing && systemNetWeight === '') {
      toast({ variant: 'destructive', title: 'System recorded net weight is missing' });
      return;
    }
    if (isExitStep && weightMismatch) {
      toast({
        variant: 'destructive',
        title: 'Weight mismatch',
        description: 'Gate clearance is blocked. Reassign the item to warehouse if it cannot exit.',
      });
      return;
    }
    const pdfWindow = isExitStep ? window.open('', '_blank') : null;
    try {
      const released = await releaseMutation.mutateAsync({
        id: item.id,
        payload: {
          reference: reference.trim() || undefined,
          bookingId: item.bookingId ?? undefined,
          customerId: undefined,
          truckPlateNumber: truckPlateNumber.trim(),
          trailerPlateNumber: trailerPlateNumber.trim() || undefined,
          driverName: driverName.trim(),
          driverLicense: driverLicense.trim() || undefined,
          driverPhone: driverPhone.trim() || undefined,
          truckType: truckType.trim() || undefined,
          containerNumber: containerNumbers.map((number) => number.trim()).filter(Boolean).join(', ') || undefined,
          gateInTime: toIsoDateTime(gateInTime),
          weighingSkipped: skipWeighing || undefined,
          tareWeight: skipWeighing ? undefined : Number(tareWeight),
          grossWeight: skipWeighing || grossWeight === '' ? undefined : Number(grossWeight),
          netWeight: !skipWeighing && isExitStep && systemNetWeight !== '' ? Number(systemNetWeight) : undefined,
          gateOutTime: isExitStep ? toIsoDateTime(gateOutTime) : undefined,
        },
      });
      if (!isExitStep) {
        toast({
          title: 'Truck arrival saved',
          description: `${released.releaseOrderReference ?? reference} is ready for exit weighing.`,
        });
        onClose();
        return;
      }
      setDownloading(true);
      const response = await warehouseService.downloadReleaseDocument(item.id);
      const blob = response.data;
      const filename = `release-${released.booking?.reference ?? released.bookingId ?? item.id}.pdf`;
      const opened = openPdfBlob(blob, filename, pdfWindow);
      toast({
        title: 'Release exit paper issued',
        description: opened
          ? 'The PDF opened in a browser tab for printing or saving.'
          : 'The browser blocked the preview tab, so the PDF was downloaded.',
      });
      onClose();
    } catch (error) {
      pdfWindow?.close();
      toast({ variant: 'destructive', title: 'Release failed', description: extractErrorMessage(error) });
    } finally {
      setDownloading(false);
    }
  };

  return (
    <Modal opened={opened} onClose={onClose} title={title} centered size="lg">
      <Stack gap="md">
        <Alert icon={<Info size={16} />} color="orange" variant="light">
          {isExitStep ? (
            <Text size="sm">
              Record the truck leaving time and gross weight. The system recorded net weight is locked,
              and the exit paper is generated only when it equals gross weight minus tare weight.
            </Text>
          ) : (
            <Text size="sm">
              Register the customer truck and driver at arrival, then save gate in time and tare weight.
              Reopen this form when the truck is leaving to complete the exit weighing.
            </Text>
          )}
        </Alert>
        <TextInput
          label="Release document reference"
          placeholder="e.g. REL-2026-001"
          value={reference}
          onChange={(e) => setReference(e.currentTarget.value)}
          readOnly={isEntranceLocked}
        />
        {noTruckAssigned && (
          <Alert color="orange" variant="light" icon={<Info size={16} />}>
            Truck is not assigned yet — assign a last-mile or customer truck, or enter the plate manually below.
          </Alert>
        )}
        {truckSelectOptions.length > 0 && (
          <Select
            label="Assigned first / last-mile truck"
            placeholder="Select the assigned truck"
            searchable
            clearable
            // Enabled at arrival so the operator picks which assigned truck came;
            // only locked on the exit (leaving) step once identity is captured.
            disabled={isEntranceLocked}
            data={truckSelectOptions}
            value={truckSelectOptions.some((truck) => truck.value === truckPlateNumber) ? truckPlateNumber : null}
            onChange={(value) => {
              const truck = truckSelectOptions.find((row) => row.value === value);
              setTruckPlateNumber(truck?.value ?? '');
              setTrailerPlateNumber(truck?.trailerPlate ?? '');
              if (truck?.driverName) setDriverName(truck.driverName);
              if (truck?.driverPhone) setDriverPhone(truck.driverPhone);
              if (truck?.truckType) setTruckType(truck.truckType);
            }}
          />
        )}
        <Group grow>
          <TextInput
            label="Truck plate number"
            required
            value={truckPlateNumber}
            onChange={(e) => setTruckPlateNumber(e.currentTarget.value)}
            readOnly={isTruckIdentityLocked}
          />
          <TextInput
            label="Trailer plate number"
            value={trailerPlateNumber}
            onChange={(e) => setTrailerPlateNumber(e.currentTarget.value)}
            readOnly={isEntranceLocked}
          />
        </Group>
        <Group grow>
          <TextInput label="Driver name" required value={driverName} onChange={(e) => setDriverName(e.currentTarget.value)} readOnly={isDriverNameLocked} />
          <TextInput label="Driver license" value={driverLicense} onChange={(e) => setDriverLicense(e.currentTarget.value)} readOnly={isEntranceLocked} />
        </Group>
        <Group grow>
          <TextInput label="Driver phone" value={driverPhone} onChange={(e) => setDriverPhone(e.currentTarget.value)} readOnly={isEntranceLocked} />
          <TextInput label="Truck type" value={truckType} onChange={(e) => setTruckType(e.currentTarget.value)} readOnly={isTruckIdentityLocked} />
        </Group>
        <Group grow align="flex-start">
          {hasContainerWeights ? (
            <MultiSelect
              label="Containers on this truck"
              description={
                isExitStep
                  ? 'Select the containers loaded on this truck — their cargo weight must match gross − tare.'
                  : 'Containers this truck will carry.'
              }
              placeholder="Select containers"
              searchable
              data={containerSelectData}
              value={selectedContainerNumbers}
              onChange={(values) => setContainerNumbers(values.length ? values : [''])}
            />
          ) : (
            <Stack gap={6}>
              <SimpleGrid cols={containerNumbers.length > 1 ? 2 : 1} spacing="sm">
                {containerNumbers.map((containerNumber, index) => (
                  <TextInput
                    key={index}
                    label={containerNumbers.length > 1 ? `Container number ${index + 1}` : 'Container number'}
                    value={containerNumber}
                    onChange={(e) =>
                      setContainerNumbers((numbers) =>
                        numbers.map((number, numberIndex) => (numberIndex === index ? e.currentTarget.value : number)),
                      )
                    }
                    readOnly={isTruckIdentityLocked}
                  />
                ))}
              </SimpleGrid>
            </Stack>
          )}
          <TextInput label="Gate in time" type="datetime-local" value={gateInTime} onChange={(e) => setGateInTime(e.currentTarget.value)} readOnly={isEntranceLocked} />
        </Group>
        {hasContainerWeights && (
          <Group gap="md" align="center">
            <Text size="sm" fw={600}>Weigh truck?</Text>
            <SegmentedControl
              size="xs"
              data={[{ value: 'yes', label: 'Yes — weigh' }, { value: 'no', label: 'No — pass' }]}
              value={weighTruck}
              onChange={(v) => setWeighTruck((v as 'yes' | 'no') ?? 'yes')}
              disabled={isEntranceLocked}
            />
            {skipWeighing && (
              <Text size="xs" c="dimmed">Weighbridge skipped — container passes without tare/gross.</Text>
            )}
          </Group>
        )}
        <Group grow>
          <NumberInput label="Tare weight (t)" required={!skipWeighing} min={0} value={tareWeight} onChange={(v) => setTareWeight(v === '' ? '' : Number(v))} readOnly={isEntranceLocked} disabled={skipWeighing} />
          <NumberInput label="Gross weight (t)" required={isExitStep && !skipWeighing} min={0} value={grossWeight} onChange={(v) => setGrossWeight(v === '' ? '' : Number(v))} disabled={!isExitStep || skipWeighing} />
          <NumberInput
            label={useContainerNet ? 'Selected cargo net (t)' : 'Recorded net weight (system t)'}
            min={0}
            value={systemNetWeight}
            readOnly
          />
        </Group>
        <Group justify="space-between">
          <Text size="sm" c={weightMismatch ? 'red' : 'dimmed'}>
            Computed net: <b>{computedNetWeight == null ? '-' : `${computedNetWeight.toLocaleString()} t`}</b>
          </Text>
          <TextInput label="Gate out time" type="datetime-local" value={gateOutTime} onChange={(e) => setGateOutTime(e.currentTarget.value)} disabled={!isExitStep} />
        </Group>
        {weightMismatch && (
          <Alert icon={<Scale size={16} />} color="red" variant="light">
            <Text size="sm">
              Weight mismatch detected. Exit paper and gate clearance are blocked; use Store or Move to
              reassign the item back to warehouse handling.
            </Text>
          </Alert>
        )}
        <Group justify="flex-end" mt="sm">
          <Button variant="default" onClick={onClose} disabled={releaseMutation.isPending || downloading}>
            Cancel
          </Button>
          <Button color="orange" onClick={handleSubmit} loading={releaseMutation.isPending || downloading}>
            {isExitStep ? 'Save Truck Leaving & View Exit Paper' : 'Save Truck Arrival'}
          </Button>
        </Group>
      </Stack>
    </Modal>
  );
}
