import { useMemo, useState } from "react";
import { Button, Dialog, DismissibleAlert, FormField, TableActionGroup, ToggleSwitch } from "@govoplan/core-webui";
import { ArrowDown, ArrowUp, Plus, Trash2 } from "lucide-react";
import type {
  CampaignPostboxCatalog,
  CampaignPostboxOrganizationUnit
} from "../../../api/campaigns";
import type { CampaignFieldDefinition } from "../utils/fieldDefinitions";
import { asRecord } from "../utils/campaignView";

export type CampaignPostboxTarget = {
  id: string;
  mode: "direct" | "derived";
  label?: string;
  postbox_id?: string;
  address_key?: string;
  template_id?: string;
  organization_unit_id?: string;
  organization_unit_field?: string;
  organization_unit_match?: "id" | "slug";
  function_id?: string;
  function_field?: string;
  function_match?: "id" | "slug";
  context_key?: string;
  context_field?: string;
};

type Props = {
  open: boolean;
  title: string;
  catalog: CampaignPostboxCatalog;
  fields: CampaignFieldDefinition[];
  targets: CampaignPostboxTarget[];
  locked?: boolean;
  merge?: boolean;
  showMerge?: boolean;
  onSave: (targets: CampaignPostboxTarget[], merge: boolean) => void;
  onClose: () => void;
};

export default function PostboxTargetsDialog({
  open,
  title,
  catalog,
  fields,
  targets,
  locked = false,
  merge = true,
  showMerge = false,
  onSave,
  onClose
}: Props) {
  const [draftTargets, setDraftTargets] = useState<CampaignPostboxTarget[]>(
    () => targets.map(normalizeTarget)
  );
  const [draftMerge, setDraftMerge] = useState(merge);
  const [error, setError] = useState("");

  function updateTarget(index: number, patch: Partial<CampaignPostboxTarget>) {
    setDraftTargets((current) =>
      current.map((target, currentIndex) =>
        currentIndex === index ? normalizeTarget({ ...target, ...patch }) : target
      )
    );
    setError("");
  }

  function addTarget() {
    const firstPostbox = catalog.postboxes[0];
    setDraftTargets((current) => [
      ...current,
      {
        id: newTargetId(),
        mode: "direct",
        postbox_id: firstPostbox?.id ?? ""
      }
    ]);
  }

  function save() {
    const prepared = draftTargets.map(normalizeTarget);
    const invalidIndex = prepared.findIndex((target) => !targetIsComplete(target));
    if (invalidIndex >= 0) {
      setError(`Target ${invalidIndex + 1} is incomplete.`);
      return;
    }
    onSave(prepared, draftMerge);
  }

  return (
    <Dialog
      open={open}
      title={title}
      className="campaign-postbox-target-modal"
      bodyClassName="campaign-postbox-target-body"
      onClose={onClose}
      footer={
        <>
          <Button onClick={onClose}>Cancel</Button>
          <Button variant="primary" disabled={locked} onClick={save}>Save</Button>
        </>
      }
    >
      <div className="campaign-postbox-target-toolbar">
        {showMerge &&
          <ToggleSwitch
            label="Postbox target defaults"
            inactiveLabel="Replace defaults"
            activeLabel="Add to defaults"
            checked={draftMerge}
            disabled={locked}
            onChange={setDraftMerge}
          />
        }
        <Button
          type="button"
          variant="primary"
          disabled={locked || catalog.postboxes.length + catalog.templates.length === 0}
          onClick={addTarget}
        >
          <Plus aria-hidden="true" />
          Add target
        </Button>
      </div>

      {error && <DismissibleAlert tone="danger" dismissible={false}>{error}</DismissibleAlert>}
      {draftTargets.length === 0 &&
        <div className="data-grid-empty">No Postbox targets configured.</div>
      }
      <div className="campaign-postbox-target-list">
        {draftTargets.map((target, index) =>
          <PostboxTargetRow
            key={target.id}
            target={target}
            index={index}
            count={draftTargets.length}
            catalog={catalog}
            fields={fields}
            locked={locked}
            onChange={(patch) => updateTarget(index, patch)}
            onMove={(offset) => setDraftTargets((current) => moveTarget(current, index, index + offset))}
            onRemove={() => setDraftTargets((current) => current.filter((_, currentIndex) => currentIndex !== index))}
          />
        )}
      </div>
    </Dialog>
  );
}

function PostboxTargetRow({
  target,
  index,
  count,
  catalog,
  fields,
  locked,
  onChange,
  onMove,
  onRemove
}: {
  target: CampaignPostboxTarget;
  index: number;
  count: number;
  catalog: CampaignPostboxCatalog;
  fields: CampaignFieldDefinition[];
  locked: boolean;
  onChange: (patch: Partial<CampaignPostboxTarget>) => void;
  onMove: (offset: number) => void;
  onRemove: () => void;
}) {
  const selectedUnit = catalog.organization_units.find(
    (unit) => unit.id === target.organization_unit_id
  );
  return (
    <section className="campaign-postbox-target-row">
      <div className="campaign-postbox-target-row-heading">
        <strong>Target {index + 1}</strong>
        <TableActionGroup actions={[
          { id: "up", label: "Move target up", icon: <ArrowUp aria-hidden="true" />, disabled: locked || index === 0, onClick: () => onMove(-1) },
          { id: "down", label: "Move target down", icon: <ArrowDown aria-hidden="true" />, disabled: locked || index === count - 1, onClick: () => onMove(1) },
          { id: "remove", label: "Remove target", icon: <Trash2 aria-hidden="true" />, disabled: locked, variant: "danger", onClick: onRemove }
        ]} />
      </div>
      <div className="form-grid compact responsive-form-grid campaign-postbox-target-grid">
        <FormField label="Target type">
          <select
            value={target.mode}
            disabled={locked}
            onChange={(event) => onChange(
              event.target.value === "derived"
                ? derivedTargetDefaults(target.id, catalog, fields)
                : directTargetDefaults(target.id, catalog)
            )}
          >
            <option value="direct">Direct Postbox</option>
            <option value="derived">Derived from fields</option>
          </select>
        </FormField>
        <FormField label="Label">
          <input
            value={target.label ?? ""}
            disabled={locked}
            onChange={(event) => onChange({ label: event.target.value })}
          />
        </FormField>
        {target.mode === "direct" ?
          <DirectTargetFields
            target={target}
            catalog={catalog}
            locked={locked}
            onChange={onChange}
          /> :
          <DerivedTargetFields
            target={target}
            catalog={catalog}
            fields={fields}
            selectedUnit={selectedUnit}
            locked={locked}
            onChange={onChange}
          />
        }
      </div>
    </section>
  );
}

function DirectTargetFields({
  target,
  catalog,
  locked,
  onChange
}: {
  target: CampaignPostboxTarget;
  catalog: CampaignPostboxCatalog;
  locked: boolean;
  onChange: (patch: Partial<CampaignPostboxTarget>) => void;
}) {
  return (
    <FormField label="Postbox">
      <select
        value={target.postbox_id ?? ""}
        disabled={locked}
        onChange={(event) => onChange({ postbox_id: event.target.value })}
      >
        <option value="">Select a Postbox</option>
        {catalog.postboxes.map((postbox) =>
          <option key={postbox.id} value={postbox.id}>
            {postbox.name} ({postbox.address}){postbox.vacant ? " - vacant" : ""}
          </option>
        )}
      </select>
    </FormField>
  );
}

function DerivedTargetFields({
  target,
  catalog,
  fields,
  selectedUnit,
  locked,
  onChange
}: {
  target: CampaignPostboxTarget;
  catalog: CampaignPostboxCatalog;
  fields: CampaignFieldDefinition[];
  selectedUnit?: CampaignPostboxOrganizationUnit;
  locked: boolean;
  onChange: (patch: Partial<CampaignPostboxTarget>) => void;
}) {
  const organizationFields = useMemo(
    () => fields.filter((field) => field.type === "organization_unit" || field.type === "string"),
    [fields]
  );
  const functionFields = useMemo(
    () => fields.filter((field) => field.type === "organization_function" || field.type === "string"),
    [fields]
  );
  return (
    <>
      <FormField label="Postbox template">
        <select
          value={target.template_id ?? ""}
          disabled={locked}
          onChange={(event) => onChange({ template_id: event.target.value })}
        >
          <option value="">Select a template</option>
          {catalog.templates.map((template) =>
            <option key={template.id} value={template.id}>{template.name}</option>
          )}
        </select>
      </FormField>
      <DerivedReferenceField
        label="Organization unit"
        fixedValue={target.organization_unit_id}
        fieldValue={target.organization_unit_field}
        match={target.organization_unit_match}
        fixedOptions={catalog.organization_units}
        fields={organizationFields}
        locked={locked}
        onFixed={(value) => onChange({
          organization_unit_id: value,
          organization_unit_field: undefined,
          function_id: undefined
        })}
        onField={(value) => onChange({
          organization_unit_id: undefined,
          organization_unit_field: value
        })}
        onMatch={(value) => onChange({ organization_unit_match: value })}
      />
      <DerivedReferenceField
        label="Function"
        fixedValue={target.function_id}
        fieldValue={target.function_field}
        match={target.function_match}
        fixedOptions={selectedUnit?.functions ?? []}
        fields={functionFields}
        locked={locked}
        fixedDisabled={!target.organization_unit_id}
        onFixed={(value) => onChange({
          function_id: value,
          function_field: undefined
        })}
        onField={(value) => onChange({
          function_id: undefined,
          function_field: value
        })}
        onMatch={(value) => onChange({ function_match: value })}
      />
      <FormField label="Context source">
        <select
          value={target.context_field ? "field" : target.context_key ? "fixed" : "none"}
          disabled={locked}
          onChange={(event) => {
            const mode = event.target.value;
            onChange({
              context_key: mode === "fixed" ? target.context_key ?? "" : undefined,
              context_field: mode === "field" ? target.context_field ?? fields[0]?.name ?? "" : undefined
            });
          }}
        >
          <option value="none">No context</option>
          <option value="fixed">Fixed value</option>
          <option value="field">Campaign field</option>
        </select>
      </FormField>
      {target.context_field !== undefined ?
        <FormField label="Context field">
          <select
            value={target.context_field}
            disabled={locked}
            onChange={(event) => onChange({ context_field: event.target.value })}
          >
            <option value="">Select a field</option>
            {fields.map((field) => <option key={field.name} value={field.name}>{field.label || field.name}</option>)}
          </select>
        </FormField> :
        target.context_key !== undefined &&
          <FormField label="Context value">
            <input
              value={target.context_key}
              disabled={locked}
              onChange={(event) => onChange({ context_key: event.target.value })}
            />
          </FormField>
      }
    </>
  );
}

function DerivedReferenceField({
  label,
  fixedValue,
  fieldValue,
  match,
  fixedOptions,
  fields,
  locked,
  fixedDisabled = false,
  onFixed,
  onField,
  onMatch
}: {
  label: string;
  fixedValue?: string;
  fieldValue?: string;
  match?: "id" | "slug";
  fixedOptions: Array<{ id: string; name: string; slug: string }>;
  fields: CampaignFieldDefinition[];
  locked: boolean;
  fixedDisabled?: boolean;
  onFixed: (value: string) => void;
  onField: (value: string) => void;
  onMatch: (value: "id" | "slug") => void;
}) {
  const usesField = fieldValue !== undefined;
  return (
    <>
      <FormField label={`${label} source`}>
        <select
          value={usesField ? "field" : "fixed"}
          disabled={locked}
          onChange={(event) =>
            event.target.value === "field"
              ? onField(fieldValue ?? fields[0]?.name ?? "")
              : onFixed(fixedValue ?? fixedOptions[0]?.id ?? "")
          }
        >
          <option value="fixed">Fixed value</option>
          <option value="field">Campaign field</option>
        </select>
      </FormField>
      {usesField ?
        <>
          <FormField label={`${label} field`}>
            <select value={fieldValue ?? ""} disabled={locked} onChange={(event) => onField(event.target.value)}>
              <option value="">Select a field</option>
              {fields.map((field) => <option key={field.name} value={field.name}>{field.label || field.name}</option>)}
            </select>
          </FormField>
          <FormField label="Field contains">
            <select value={match ?? "id"} disabled={locked} onChange={(event) => onMatch(event.target.value as "id" | "slug")}>
              <option value="id">Stable ID</option>
              <option value="slug">Key / slug</option>
            </select>
          </FormField>
        </> :
        <FormField label={label}>
          <select value={fixedValue ?? ""} disabled={locked || fixedDisabled} onChange={(event) => onFixed(event.target.value)}>
            <option value="">Select {label.toLowerCase()}</option>
            {fixedOptions.map((option) => <option key={option.id} value={option.id}>{option.name} ({option.slug})</option>)}
          </select>
        </FormField>
      }
    </>
  );
}

export function normalizePostboxTargets(value: unknown): CampaignPostboxTarget[] {
  if (!Array.isArray(value)) return [];
  return value
    .map(asRecord)
    .map((target) => normalizeTarget({
      id: String(target.id ?? ""),
      mode: target.mode === "derived" ? "derived" : "direct",
      label: optionalText(target.label),
      postbox_id: optionalText(target.postbox_id),
      address_key: optionalText(target.address_key),
      template_id: optionalText(target.template_id),
      organization_unit_id: optionalText(target.organization_unit_id),
      organization_unit_field: optionalText(target.organization_unit_field),
      organization_unit_match: target.organization_unit_match === "slug" ? "slug" : "id",
      function_id: optionalText(target.function_id),
      function_field: optionalText(target.function_field),
      function_match: target.function_match === "slug" ? "slug" : "id",
      context_key: optionalText(target.context_key),
      context_field: optionalText(target.context_field)
    }))
    .filter((target) => Boolean(target.id));
}

function normalizeTarget(target: CampaignPostboxTarget): CampaignPostboxTarget {
  const common = {
    id: target.id || newTargetId(),
    mode: target.mode,
    ...(target.label?.trim() ? { label: target.label.trim() } : {})
  };
  if (target.mode === "direct") {
    return {
      ...common,
      mode: "direct",
      ...(target.postbox_id ? { postbox_id: target.postbox_id } : {}),
      ...(!target.postbox_id && target.address_key ? { address_key: target.address_key } : {})
    };
  }
  return {
    ...common,
    mode: "derived",
    template_id: target.template_id ?? "",
    ...(target.organization_unit_field !== undefined
      ? {
          organization_unit_field: target.organization_unit_field,
          organization_unit_match: target.organization_unit_match ?? "id"
        }
      : { organization_unit_id: target.organization_unit_id ?? "" }),
    ...(target.function_field !== undefined
      ? {
          function_field: target.function_field,
          function_match: target.function_match ?? "id"
        }
      : { function_id: target.function_id ?? "" }),
    ...(target.context_field !== undefined
      ? { context_field: target.context_field }
      : target.context_key !== undefined
        ? { context_key: target.context_key }
        : {})
  };
}

function targetIsComplete(target: CampaignPostboxTarget): boolean {
  if (!target.id) return false;
  if (target.mode === "direct") {
    return Boolean(target.postbox_id || target.address_key);
  }
  return Boolean(
    target.template_id
    && (target.organization_unit_id || target.organization_unit_field)
    && (target.function_id || target.function_field)
  );
}

function directTargetDefaults(id: string, catalog: CampaignPostboxCatalog): CampaignPostboxTarget {
  return {
    id,
    mode: "direct",
    postbox_id: catalog.postboxes[0]?.id ?? ""
  };
}

function derivedTargetDefaults(
  id: string,
  catalog: CampaignPostboxCatalog,
  fields: CampaignFieldDefinition[]
): CampaignPostboxTarget {
  const unit = catalog.organization_units[0];
  return {
    id,
    mode: "derived",
    template_id: catalog.templates[0]?.id ?? "",
    ...(unit
      ? {
          organization_unit_id: unit.id,
          function_id: unit.functions[0]?.id ?? ""
        }
      : {
          organization_unit_field: fields[0]?.name ?? "",
          organization_unit_match: "id",
          function_field: fields[0]?.name ?? "",
          function_match: "id"
        })
  };
}

function moveTarget(
  targets: CampaignPostboxTarget[],
  source: number,
  destination: number
): CampaignPostboxTarget[] {
  if (destination < 0 || destination >= targets.length) return targets;
  const next = [...targets];
  const [target] = next.splice(source, 1);
  next.splice(destination, 0, target);
  return next;
}

function optionalText(value: unknown): string | undefined {
  const text = String(value ?? "").trim();
  return text || undefined;
}

function newTargetId(): string {
  const suffix = typeof crypto !== "undefined" && "randomUUID" in crypto
    ? crypto.randomUUID()
    : `${Date.now()}-${Math.random().toString(16).slice(2)}`;
  return `postbox-${suffix}`;
}
