import { useEffect, useMemo, useState } from "react";
import { Loader2, Calendar, ShieldCheck } from "lucide-react";
import {
  Alert,
  Badge,
  Button,
  Group,
  Modal,
  NumberInput,
  Select,
  MultiSelect,
  SimpleGrid,
  Stack,
  Text,
  Textarea,
  TextInput,
  ActionIcon,
} from "@mantine/core";

import {
  FLEET_SELECT_NONE,
  type FleetFormFieldDef,
} from "@/pages/fleet/config/resources";
import type { FleetRecord } from "@/services/fleet/fleet.service";
import {
  verifaydaService,
  type FaydaCallbackMessage,
} from "@/services/verifayda.service";

export interface FleetFormDialogProps {
  open: boolean;
  onOpenChange: (open: boolean) => void;
  title: string;
  fields: FleetFormFieldDef[];
  initialRecord?: FleetRecord | null;
  emptyValues: Record<string, unknown>;
  isSubmitting: boolean;
  selectOptionsLoading?: boolean;
  onSubmit: (values: Record<string, unknown>) => void;
  /**
   * Show a "Verify with Fayda" step: opens the eSignet popup and prefills
   * firstName/lastName/email/phoneNumber/dateOfBirth from the verified
   * identity, stamping faydaVerified + faydaSub on the payload.
   */
  verifyWithFayda?: boolean;
}

// Fayda returns gender as "Male"/"Female"; snap it onto the form's uppercase
// option values (MALE/FEMALE/OTHER) so the Select prefills instead of rendering
// blank. Unknown/empty values fall through to undefined (field left untouched).
const normalizeGender = (raw?: string): string | undefined => {
  const up = (raw ?? "").trim().toUpperCase();
  if (up === "MALE" || up === "M") return "MALE";
  if (up === "FEMALE" || up === "F") return "FEMALE";
  return up ? "OTHER" : undefined;
};

// Fayda may return the birthdate as "2001/12/01" (slashes), but the date input
// and validator expect ISO "2001-12-01". Normalize separators + trim to 10 chars
// so the DOB field prefills instead of silently staying blank.
const normalizeBirthdate = (raw?: string): string | undefined => {
  const iso = (raw ?? "").trim().replace(/\//g, "-").slice(0, 10);
  return /^\d{4}-\d{2}-\d{2}$/.test(iso) ? iso : undefined;
};

const buildInitialValues = (
  fields: FleetFormFieldDef[],
  emptyValues: Record<string, unknown>,
  record?: FleetRecord | null,
): Record<string, unknown> => {
  const values: Record<string, unknown> = { ...emptyValues };
  if (!record) return values;

  fields.forEach((field) => {
    const raw = (record as unknown as Record<string, unknown>)[field.name];
    if (raw === null || raw === undefined) {
      values[field.name] = field.noneOption ? FLEET_SELECT_NONE : "";
      return;
    }
    // For selects, snap the record value onto a real option even if its casing
    // drifted (e.g. an API/seed value of "Available" vs the "AVAILABLE" option).
    // Otherwise the Select renders blank and a required field fails on submit.
    if (field.type === "select" && field.options?.length) {
      const match = field.options.find(
        (o) => String(o.value).toLowerCase() === String(raw).toLowerCase(),
      );
      values[field.name] = match ? match.value : raw;
      return;
    }
    values[field.name] = raw;
  });
  return values;
};

const FleetFormDialog = ({
  open,
  onOpenChange,
  title,
  fields,
  initialRecord,
  emptyValues,
  isSubmitting,
  selectOptionsLoading,
  onSubmit,
  verifyWithFayda,
}: FleetFormDialogProps) => {
  const [values, setValues] = useState<Record<string, unknown>>({});
  const [errors, setErrors] = useState<Record<string, string>>({});
  const [faydaLoading, setFaydaLoading] = useState(false);
  const [faydaError, setFaydaError] = useState<string | null>(null);

  // Seed the form ONLY when the dialog opens or the edited record changes — NOT
  // when `fields`/`emptyValues` get new object refs (they're rebuilt whenever the
  // dynamic select options finish loading). Re-seeding on those would wipe the
  // user's in-progress edits (e.g. a changed Current Yard / status) the moment
  // the yard or wagon-type options resolve.
  const recordId =
    initialRecord && "id" in initialRecord ? String(initialRecord.id) : null;
  useEffect(() => {
    if (open) {
      setValues(buildInitialValues(fields, emptyValues, initialRecord));
      setErrors({});
      setFaydaError(null);
      setFaydaLoading(false);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [open, recordId]);

  // Receive the ?code&state relayed by the /callback popup, exchange it for
  // the verified identity, and prefill the matching form fields.
  useEffect(() => {
    if (!open || !verifyWithFayda) return;
    const onMessage = async (event: MessageEvent<FaydaCallbackMessage>) => {
      if (event.origin !== window.location.origin) return;
      if (event.data?.type !== "fayda-callback") return;

      if (event.data.error) {
        setFaydaLoading(false);
        setFaydaError(event.data.errorDescription ?? event.data.error);
        return;
      }
      if (!event.data.code || !event.data.state) return;

      try {
        const result = await verifaydaService.complete(event.data.code, event.data.state);
        if (!result.verified) {
          setFaydaError("Identity could not be verified");
          return;
        }
        const nameParts = (result.fullName ?? "").trim().split(/\s+/).filter(Boolean);
        const [firstName, ...rest] = nameParts;
        const gender = normalizeGender(result.gender);
        const dateOfBirth = normalizeBirthdate(result.birthdate);
        setValues((current) => ({
          ...current,
          ...(firstName ? { firstName } : {}),
          ...(rest.length ? { lastName: rest.join(" ") } : {}),
          ...(result.email ? { email: result.email } : {}),
          ...(result.phoneNumber ? { phoneNumber: result.phoneNumber } : {}),
          ...(dateOfBirth ? { dateOfBirth } : {}),
          ...(gender ? { gender } : {}),
          faydaVerified: true,
          ...(result.iamUserId ? { faydaSub: result.iamUserId } : {}),
        }));
        setFaydaError(null);
      } catch (err) {
        const message =
          (err as { response?: { data?: { message?: string } } })?.response?.data?.message ??
          (err instanceof Error ? err.message : "Verification failed");
        setFaydaError(message);
      } finally {
        setFaydaLoading(false);
      }
    };
    window.addEventListener("message", onMessage);
    return () => window.removeEventListener("message", onMessage);
  }, [open, verifyWithFayda]);

  const handleFaydaVerify = async () => {
    setFaydaError(null);
    setFaydaLoading(true);
    try {
      const { authorizationUrl } = await verifaydaService.start();
      const popup = window.open(
        authorizationUrl,
        "fayda-verify",
        "width=480,height=760,noopener=no",
      );
      if (!popup) {
        setFaydaLoading(false);
        setFaydaError("Pop-up blocked — allow pop-ups for this site and retry.");
      }
      // Loading stays on until the popup posts back; reopening the dialog resets it.
    } catch (err) {
      setFaydaLoading(false);
      const message =
        (err as { response?: { data?: { message?: string } } })?.response?.data?.message ??
        (err instanceof Error ? err.message : "Could not start verification");
      setFaydaError(message);
    }
  };

  const faydaVerified = values.faydaVerified === true;

  const shortFields = useMemo(
    () => fields.filter((f) => f.type !== "textarea"),
    [fields],
  );
  const longFields = useMemo(
    () => fields.filter((f) => f.type === "textarea"),
    [fields],
  );

  const validate = () => {
    const next: Record<string, string> = {};
    fields.forEach((field) => {
      const value = values[field.name];
      const stringValue =
        typeof value === "string" ? value.trim() : String(value ?? "");

      if (
        field.required &&
        (stringValue === "" || stringValue === FLEET_SELECT_NONE)
      ) {
        next[field.name] = `${field.label} is required`;
      }

      // Validate date format (YYYY-MM-DD) - DatePicker ensures this
      if (
        field.type === "date" &&
        stringValue &&
        stringValue !== FLEET_SELECT_NONE
      ) {
        const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
        if (!dateRegex.test(stringValue)) {
          next[field.name] = `${field.label} must be a valid date`;
        } else {
          const date = new Date(stringValue + "T00:00:00Z");
          if (isNaN(date.getTime())) {
            next[field.name] = `${field.label} is not a valid date`;
          } else if (date > new Date()) {
            next[field.name] = `${field.label} cannot be in the future`;
          }
        }
      }
    });
    setErrors(next);
    return Object.keys(next).length === 0;
  };

  // Field types keyed by name, so the submit payload can coerce each value to the
  // type the API expects (number columns come back from the API as strings like
  // "24.00", which the DTO's @IsNumber rejects on an otherwise-unchanged save).
  const fieldTypeByName = useMemo(() => {
    const map: Record<string, FleetFormFieldDef["type"]> = {};
    fields.forEach((f) => (map[f.name] = f.type));
    return map;
  }, [fields]);

  const handleSubmit = () => {
    if (!validate()) return;
    const payload = Object.fromEntries(
      Object.entries(values)
        .map(([key, value]) => {
          if (value === FLEET_SELECT_NONE || value === "")
            return [key, undefined];
          if (fieldTypeByName[key] === "number") {
            const num = Number(value);
            return [key, Number.isNaN(num) ? undefined : num];
          }
          return [key, value];
        })
        .filter(([, value]) => value !== undefined),
    );
    onSubmit(payload);
  };

  const renderField = (field: FleetFormFieldDef) => {
    const value = values[field.name];
    const error = errors[field.name];

    if (field.type === "select") {
      return (
        <Select
          key={field.name}
          label={field.label}
          data={field.options ?? []}
          value={
            value == null || value === ""
              ? field.noneOption
                ? FLEET_SELECT_NONE
                : null
              : String(value)
          }
          onChange={(next) =>
            setValues((current) => ({ ...current, [field.name]: next ?? "" }))
          }
          error={error}
          searchable
          disabled={selectOptionsLoading}
          rightSection={
            selectOptionsLoading ? (
              <Loader2 size={14} className="animate-spin" />
            ) : undefined
          }
        />
      );
    }

    if (field.type === "multiselect") {
      const arrayValue = Array.isArray(value)
        ? value
        : typeof value === "string" && value
          ? [value]
          : [];

      return (
        <MultiSelect
          key={field.name}
          label={field.label}
          placeholder={field.placeholder || "Select options"}
          data={field.options ?? []}
          value={arrayValue.map(String)}
          onChange={(next) =>
            setValues((current) => ({ ...current, [field.name]: next }))
          }
          error={error}
          searchable
          clearable
          disabled={selectOptionsLoading}
          rightSection={
            selectOptionsLoading ? (
              <Loader2 size={14} className="animate-spin" />
            ) : undefined
          }
        />
      );
    }

    if (field.type === "number") {
      return (
        <NumberInput
          key={field.name}
          label={field.label}
          placeholder={field.placeholder}
          value={value === "" || value == null ? "" : Number(value)}
          onChange={(next) =>
            setValues((current) => ({
              ...current,
              [field.name]: next === "" ? "" : next,
            }))
          }
          error={error}
          disabled={field.disabled}
        />
      );
    }

    if (field.type === "textarea") {
      return (
        <Textarea
          key={field.name}
          label={field.label}
          placeholder={field.placeholder}
          value={String(value ?? "")}
          onChange={(e) =>
            setValues((current) => ({
              ...current,
              [field.name]: e.currentTarget?.value,
            }))
          }
          error={error}
          minRows={3}
          disabled={field.disabled}
        />
      );
    }

    if (field.type === "date") {
      return (
        <TextInput
          key={field.name}
          type="date"
          label={field.label}
          placeholder={field.placeholder || "YYYY-MM-DD"}
          value={typeof value === "string" ? value.slice(0, 10) : ""}
          onChange={(e) =>
            setValues((current) => ({
              ...current,
              [field.name]: e.currentTarget?.value ?? "",
            }))
          }
          error={error}
          disabled={field.disabled}
          description={field.description || "Select a date"}
          rightSection={
            <ActionIcon size="sm" variant="subtle" color="green">
              <Calendar size={16} />
            </ActionIcon>
          }
          size="sm"
          radius="md"
          styles={{
            input: {
              borderColor: "var(--mantine-color-gray-3)",
              backgroundColor: "var(--mantine-color-gray-0)",
              transition: "all 200ms ease",
              "&:focus": {
                borderColor: "var(--mantine-color-green-5)",
                backgroundColor: "var(--mantine-color-gray-1)",
              },
            },
          }}
        />
      );
    }

    return (
      <TextInput
        key={field.name}
        label={field.label}
        placeholder={field.placeholder}
        description={field.description}
        value={String(value ?? "")}
        onChange={(e) =>
          setValues((current) => ({
            ...current,
            [field.name]: e.currentTarget?.value,
          }))
        }
        error={error}
        disabled={field.disabled}
      />
    );
  };

  return (
    <Modal
      opened={open}
      onClose={() => onOpenChange(false)}
      title={<Text fw={600}>{title}</Text>}
      size="lg"
      radius="lg"
      centered
    >
      <Stack gap="md">
        {verifyWithFayda && (
          <Group justify="space-between" wrap="nowrap">
            {faydaVerified ? (
              <Badge
                color="green"
                variant="light"
                size="lg"
                leftSection={<ShieldCheck size={14} />}
              >
                Identity verified with Fayda
              </Badge>
            ) : (
              <Text size="sm" c="dimmed">
                Verify the driver's identity with Fayda to prefill their details.
              </Text>
            )}
            <Button
              variant={faydaVerified ? "default" : "light"}
              color="edr-green"
              size="xs"
              leftSection={<ShieldCheck size={14} />}
              loading={faydaLoading}
              onClick={handleFaydaVerify}
            >
              {faydaVerified ? "Re-verify" : "Verify with Fayda"}
            </Button>
          </Group>
        )}
        {verifyWithFayda && faydaError && (
          <Alert color="red" variant="light">
            {faydaError}
          </Alert>
        )}
        <SimpleGrid cols={{ base: 1, sm: 2 }} spacing="md">
          {shortFields.map(renderField)}
        </SimpleGrid>
        {longFields.map(renderField)}
        <Group justify="flex-end" gap="sm" mt="sm">
          <Button variant="default" onClick={() => onOpenChange(false)}>
            Cancel
          </Button>
          <Button
            color="edr-green"
            loading={isSubmitting}
            onClick={handleSubmit}
          >
            Save
          </Button>
        </Group>
      </Stack>
    </Modal>
  );
};

export default FleetFormDialog;
