import { useEffect, useMemo, useState } from "react";
import { Loader2 } from "lucide-react";
import {
  Modal,
  Button,
  TextInput,
  Textarea,
  Select,
  Switch,
  Stack,
  Group,
  Text,
  Box,
  SimpleGrid,
  Divider,
} from "@mantine/core";

import {
  RULE_ENGINE_SELECT_NONE,
  type FormFieldDef,
} from "@/pages/ruleEngine/config/resources";
import type { RuleEngineRecord } from "@/types/rule-engine";
import { RULE_ENGINE_POSITION_END } from "./ruleEngineOrder.utils";

export interface RuleEngineFormDialogProps {
  open: boolean;
  onOpenChange: (open: boolean) => void;
  title: string;
  description: string;
  fields: FormFieldDef[];
  initialRecord?: RuleEngineRecord | null;
  isSubmitting: boolean;
  selectOptionsLoading?: boolean;
  positionOptions?: { label: string; value: string }[];
  positionLoading?: boolean;
  onSubmit: (values: Record<string, unknown>) => void;
}

type FormRow =
  | { kind: "pair"; fields: [FormFieldDef, FormFieldDef] }
  | { kind: "single"; field: FormFieldDef };

const isShortField = (field: FormFieldDef) =>
  field.type === "text" ||
  field.type === "number" ||
  field.type === "select" ||
  field.type === "date";

const buildFormRows = (fields: FormFieldDef[]): FormRow[] => {
  const rows: FormRow[] = [];
  let index = 0;

  while (index < fields.length) {
    const field = fields[index];

    if (field.type === "textarea" || field.type === "boolean") {
      rows.push({ kind: "single", field });
      index += 1;
      continue;
    }

    const next = fields[index + 1];
    if (next && isShortField(next)) {
      rows.push({ kind: "pair", fields: [field, next] });
      index += 2;
      continue;
    }

    rows.push({ kind: "single", field });
    index += 1;
  }

  return rows;
};

const buildInitialValues = (
  fields: FormFieldDef[],
  record?: RuleEngineRecord | null,
): Record<string, unknown> => {
  const values: Record<string, unknown> = {};
  for (const field of fields) {
    const raw = record?.[field.name];
    if (raw !== undefined && raw !== null) {
      if (field.type === "date" && typeof raw === "string") {
        values[field.name] = raw.slice(0, 10);
      } else if (Array.isArray(raw)) {
        values[field.name] = raw.join(", ");
      } else {
        values[field.name] = raw;
      }
    } else if (field.type === "boolean") {
      values[field.name] = false;
    } else if (field.type === "number") {
      values[field.name] = "";
    } else {
      values[field.name] = "";
    }
  }
  return values;
};

const resolveSelectValue = (
  field: FormFieldDef,
  values: Record<string, unknown>,
): string | undefined => {
  const raw = values[field.name];
  const isEmpty = raw === "" || raw === null || raw === undefined;

  if (field.optional && isEmpty) {
    return RULE_ENGINE_SELECT_NONE;
  }

  if (isEmpty) {
    return undefined;
  }

  return String(raw);
};

const inputStyles = {
  label: { fontWeight: 600, marginBottom: 6, color: "var(--mantine-color-gray-8)" },
} as const;

const FieldLabel = ({ label, required }: { label: string; required?: boolean }) => (
  <Group gap={4} wrap="nowrap">
    <span>{label}</span>
    {required ? (
      <Text component="span" c="red" size="sm">
        *
      </Text>
    ) : null}
  </Group>
);

const RuleEngineFormDialog = ({
  open,
  onOpenChange,
  title,
  fields,
  initialRecord,
  isSubmitting,
  selectOptionsLoading = false,
  positionOptions,
  positionLoading = false,
  onSubmit,
}: RuleEngineFormDialogProps) => {
  const [values, setValues] = useState<Record<string, unknown>>(() =>
    buildInitialValues(fields, initialRecord),
  );
  const [position, setPosition] = useState(RULE_ENGINE_POSITION_END);

  useEffect(() => {
    if (open) {
      setValues(buildInitialValues(fields, initialRecord));
      setPosition(RULE_ENGINE_POSITION_END);
    }
  }, [open, fields, initialRecord]);

  const visibleFields = useMemo(
    () =>
      fields.filter((field) => {
        if (
          field.hideWhen &&
          field.hideWhen.equals.includes(String(values[field.hideWhen.field] ?? ""))
        ) {
          return false;
        }
        if (
          field.showWhen &&
          !field.showWhen.equals.includes(String(values[field.showWhen.field] ?? ""))
        ) {
          return false;
        }
        return true;
      }),
    [fields, values],
  );

  const formRows = useMemo(() => buildFormRows(visibleFields), [visibleFields]);

  const setField = (name: string, value: unknown) => {
    setValues((current) => ({ ...current, [name]: value }));
  };

  const handleSubmit = (event: React.FormEvent) => {
    event.preventDefault();
    const payload: Record<string, unknown> = {};

    for (const field of visibleFields) {
      const raw = values[field.name];
      if (field.type === "number") {
        if (raw === "" || raw === undefined) continue;
        payload[field.name] = Number(raw);
      } else if (field.type === "boolean") {
        payload[field.name] = Boolean(raw);
      } else if (
        field.type === "select" &&
        (raw === "" || raw === RULE_ENGINE_SELECT_NONE)
      ) {
        if (!field.required) continue;
      } else if (raw === "" || raw === undefined) {
        if (!field.required) continue;
        payload[field.name] = raw;
      } else {
        payload[field.name] = raw;
      }
    }

    if (fields.some((f) => f.name === "code" && typeof payload.code === "string")) {
      payload.code = String(payload.code).toUpperCase();
    }

    if (!initialRecord && positionOptions && position !== RULE_ENGINE_POSITION_END) {
      payload.insertAfterId = position;
    }

    onSubmit(payload);
  };

  const renderField = (field: FormFieldDef) => {
    if (field.type === "boolean") {
      return (
        <Group
          key={field.name}
          justify="space-between"
          align="center"
          wrap="nowrap"
          gap="md"
          px="md"
          style={{
            minHeight: 42,
            background: "var(--mantine-color-gray-0)",
            border: "1px solid var(--mantine-color-gray-3)",
            borderRadius: "var(--mantine-radius-md)",
          }}
        >
          <Text size="sm" fw={600}>
            {field.label}
          </Text>
          <Switch
            checked={Boolean(values[field.name])}
            onChange={(e) => setField(field.name, e.currentTarget.checked)}
            size="md"
            color="edr-green"
          />
        </Group>
      );
    }

    const label = <FieldLabel label={field.label} required={field.required} />;

    if (field.type === "select") {
      return (
        <Select
          key={field.name}
          label={label}
          description={field.description}
          placeholder={
            selectOptionsLoading ? "Loading options..." : (field.placeholder ?? "Select an option")
          }
          value={resolveSelectValue(field, values)}
          onChange={(v) => setField(field.name, v === RULE_ENGINE_SELECT_NONE ? "" : v)}
          disabled={selectOptionsLoading}
          data={(field.options ?? [])
            .filter((opt) => opt.value !== "")
            .map((opt) => ({
              label: opt.label,
              value: opt.value,
            }))}
          searchable
          clearable
          size="md"
          radius="md"
          styles={inputStyles}
        />
      );
    }

    if (field.type === "textarea") {
      return (
        <Textarea
          key={field.name}
          label={label}
          value={String(values[field.name] ?? "")}
          onChange={(e) => setField(field.name, e.currentTarget.value)}
          placeholder={field.placeholder}
          required={field.required}
          minRows={4}
          autosize
          maxRows={8}
          size="md"
          radius="md"
          styles={inputStyles}
        />
      );
    }

    return (
      <TextInput
        key={field.name}
        label={label}
        type={field.type === "number" ? "number" : field.type === "date" ? "date" : "text"}
        value={String(values[field.name] ?? "")}
        onChange={(e) => setField(field.name, e.currentTarget.value)}
        placeholder={field.placeholder}
        required={field.required}
        size="md"
        radius="md"
        styles={inputStyles}
      />
    );
  };

  return (
    <Modal
      opened={open}
      onClose={() => onOpenChange(false)}
      title={
        <Text size="lg" fw={700} lh={1.2}>
          {title}
        </Text>
      }
      centered
      size={720}
      radius="lg"
      padding="xl"
      overlayProps={{ backgroundOpacity: 0.45, blur: 3 }}
      styles={{
        content: {
          maxWidth: "min(720px, 95vw)",
        },
        body: {
          paddingTop: 20,
        },
      }}
    >
      <form onSubmit={handleSubmit}>
        <Stack gap="lg">
          <Box style={{ maxHeight: "calc(65vh - 120px)", overflowY: "auto", paddingRight: 4 }}>
            <Stack gap="md">
              {!initialRecord && positionOptions ? (
                <Select
                  label="Position"
                  description="New items are appended to the end by default."
                  value={position}
                  onChange={(value) => setPosition(value ?? RULE_ENGINE_POSITION_END)}
                  data={[
                    { label: "At end (default)", value: RULE_ENGINE_POSITION_END },
                    ...positionOptions,
                  ]}
                  searchable
                  disabled={positionLoading}
                  size="md"
                  radius="md"
                  styles={inputStyles}
                />
              ) : null}
              {formRows.map((row) =>
                row.kind === "pair" ? (
                  <SimpleGrid key={`${row.fields[0].name}-${row.fields[1].name}`} cols={2} spacing="md">
                    <Box style={{ minWidth: 0 }}>{renderField(row.fields[0])}</Box>
                    <Box style={{ minWidth: 0 }}>{renderField(row.fields[1])}</Box>
                  </SimpleGrid>
                ) : (
                  <Box key={row.field.name}>{renderField(row.field)}</Box>
                ),
              )}
            </Stack>
          </Box>

          <Divider />

          <Group justify="flex-end" gap="sm">
            <Button
              variant="default"
              onClick={() => onOpenChange(false)}
              disabled={isSubmitting}
              radius="md"
              size="md"
            >
              Cancel
            </Button>
            <Button
              type="submit"
              disabled={isSubmitting}
              leftSection={
                isSubmitting ? (
                  <Loader2 size={18} style={{ animation: "spin 1s linear infinite" }} />
                ) : undefined
              }
              radius="md"
              color="edr-green"
              variant="filled"
              fw={600}
              size="md"
            >
              {isSubmitting ? "Saving..." : "Save"}
            </Button>
          </Group>
        </Stack>
      </form>
    </Modal>
  );
};

export default RuleEngineFormDialog;
