import { useCallback, useEffect, useMemo, useRef, useState, type FormEvent } from "react";
import { Edit3, RefreshCw, Save, XCircle } from "lucide-react";
import {
  ApiError,
  Button,
  Card,
  DataGrid,
  DismissibleAlert,
  FormField,
  LoadingFrame,
  PageTitle,
  StatusBadge,
  hasScope,
  useUnsavedDraftGuard,
  type ApiSettings,
  type AuthInfo,
  type DataGridColumn
} from "@govoplan/core-webui";
import {
  createOrganizationFunctionAssignment,
  getIdmSettings,
  getOrganizationFunctionAssignments,
  getOrganizationModel,
  patchIdmSettings,
  patchOrganizationFunctionAssignment,
  searchOrganizationIdentityOptions,
  type IdmSettings,
  type IdentityOption,
  type OrganizationFunctionAssignmentItem,
  type OrganizationFunctionAssignmentPayload,
  type OrganizationFunctionItem,
  type OrganizationModel,
  type OrganizationUnitItem
} from "../api/idm";

type IdmPageProps = {
  settings: ApiSettings;
  auth: AuthInfo;
};

type AssignmentDraft = {
  identity_id: string;
  account_id: string;
  function_id: string;
  applies_to_subunits: boolean;
  source: string;
  delegated_from_assignment_id: string;
  acting_for_account_id: string;
  is_active: boolean;
};

type SettingsDraft = {
  require_assignment_change_requests: boolean;
  audit_detail_level: "summary" | "standard" | "full";
  change_retention_days: string;
};

const EMPTY_MODEL: OrganizationModel = {
  units: [],
  functions: []
};

const DEFAULT_IDM_SETTINGS: IdmSettings = {
  tenant_id: "",
  require_assignment_change_requests: false,
  audit_detail_level: "standard",
  change_retention_days: null,
  settings: {}
};

const SOURCE_OPTIONS = [
  { value: "direct", label: "i18n:govoplan-idm.direct.7b2b1f51" },
  { value: "delegated", label: "i18n:govoplan-idm.delegated.b189f4b6" },
  { value: "acting_for", label: "i18n:govoplan-idm.acting_for.8650e6a6" },
  { value: "directory", label: "i18n:govoplan-idm.directory.12e2e859" },
  { value: "governance", label: "i18n:govoplan-idm.governance.b989a277" },
  { value: "system", label: "i18n:govoplan-idm.system.70c07e3f" }
];

function emptyAssignmentDraft(): AssignmentDraft {
  return {
    identity_id: "",
    account_id: "",
    function_id: "",
    applies_to_subunits: false,
    source: "direct",
    delegated_from_assignment_id: "",
    acting_for_account_id: "",
    is_active: true
  };
}

function textOrNull(value: string): string | null {
  const trimmed = value.trim();
  return trimmed ? trimmed : null;
}

function assignmentPayload(draft: AssignmentDraft): OrganizationFunctionAssignmentPayload {
  return {
    identity_id: draft.identity_id,
    account_id: textOrNull(draft.account_id),
    function_id: draft.function_id,
    applies_to_subunits: draft.applies_to_subunits,
    source: draft.source,
    delegated_from_assignment_id: textOrNull(draft.delegated_from_assignment_id),
    acting_for_account_id: textOrNull(draft.acting_for_account_id),
    is_active: draft.is_active,
    settings: {}
  };
}

function assignmentDraftFrom(item: OrganizationFunctionAssignmentItem): AssignmentDraft {
  return {
    identity_id: item.identity_id,
    account_id: item.account_id ?? "",
    function_id: item.function_id,
    applies_to_subunits: item.applies_to_subunits,
    source: item.source || "direct",
    delegated_from_assignment_id: item.delegated_from_assignment_id ?? "",
    acting_for_account_id: item.acting_for_account_id ?? "",
    is_active: item.is_active
  };
}

function isAssignmentDirty(draft: AssignmentDraft): boolean {
  return Boolean(
    draft.identity_id ||
    draft.account_id ||
    draft.function_id ||
    draft.applies_to_subunits ||
    draft.source !== "direct" ||
    draft.delegated_from_assignment_id ||
    draft.acting_for_account_id ||
    !draft.is_active
  );
}

function settingsDraftFrom(item: IdmSettings | null): SettingsDraft {
  const source = item ?? DEFAULT_IDM_SETTINGS;
  return {
    require_assignment_change_requests: source.require_assignment_change_requests,
    audit_detail_level: source.audit_detail_level,
    change_retention_days: source.change_retention_days == null ? "" : String(source.change_retention_days)
  };
}

function settingsPayload(draft: SettingsDraft, item: IdmSettings | null): Pick<IdmSettings, "require_assignment_change_requests" | "audit_detail_level" | "change_retention_days" | "settings"> {
  const trimmedDays = draft.change_retention_days.trim();
  return {
    require_assignment_change_requests: draft.require_assignment_change_requests,
    audit_detail_level: draft.audit_detail_level,
    change_retention_days: trimmedDays ? Number(trimmedDays) : null,
    settings: item?.settings ?? {}
  };
}

function isSettingsDirty(draft: SettingsDraft, item: IdmSettings | null): boolean {
  const baseline = settingsDraftFrom(item);
  return (
    draft.require_assignment_change_requests !== baseline.require_assignment_change_requests ||
    draft.audit_detail_level !== baseline.audit_detail_level ||
    draft.change_retention_days.trim() !== baseline.change_retention_days.trim()
  );
}

function mapById<T extends { id: string }>(items: T[]): Map<string, T> {
  return new Map(items.map((item) => [item.id, item]));
}

function apiErrorMessage(error: unknown): string {
  if (error instanceof ApiError) {
    try {
      const parsed = JSON.parse(error.body) as { detail?: string | { message?: string } };
      if (typeof parsed.detail === "string") return parsed.detail;
      if (parsed.detail && typeof parsed.detail.message === "string") return parsed.detail.message;
    } catch {
      // Fall back to the transport message below.
    }
    return error.message;
  }
  if (error instanceof Error) return error.message;
  return String(error);
}

function identityDisplay(identity: IdentityOption | undefined, fallbackId: string): JSX.Element {
  if (!identity) return <span className="idm-id">{fallbackId}</span>;
  const label = identity.display_name || identity.external_subject || identity.id;
  return (
    <span className="idm-identity">
      <span>{label}</span>
      <span className="idm-id">{identity.id}</span>
    </span>
  );
}

function functionLabel(item: OrganizationFunctionItem, unitById: ReadonlyMap<string, OrganizationUnitItem>): string {
  const unitName = unitById.get(item.organization_unit_id)?.name;
  return unitName ? `${item.name} (${unitName})` : item.name;
}

function assignmentOptionLabel(
  item: OrganizationFunctionAssignmentItem,
  identityById: ReadonlyMap<string, IdentityOption>,
  functionById: ReadonlyMap<string, OrganizationFunctionItem>,
  unitById: ReadonlyMap<string, OrganizationUnitItem>
): string {
  const identity = identityById.get(item.identity_id);
  const identityLabel = identity?.display_name || identity?.external_subject || item.identity_id;
  const functionItem = functionById.get(item.function_id);
  const functionText = functionItem ? functionLabel(functionItem, unitById) : item.function_id;
  return `${identityLabel} - ${functionText}`;
}

function activeStatus(active: boolean): JSX.Element {
  return <StatusBadge status={active ? "success" : "inactive"} label={active ? "i18n:govoplan-idm.active.7bd0e9f8" : "i18n:govoplan-idm.inactive.1baa5fba"} />;
}

function sourceLabel(value: string): string {
  return SOURCE_OPTIONS.find((item) => item.value === value)?.label ?? value;
}

function idmInitialQuery(): { assignmentId: string; functionId: string } {
  if (typeof window === "undefined") return { assignmentId: "", functionId: "" };
  const params = new URLSearchParams(window.location.search);
  return {
    assignmentId: params.get("assignment_id") || "",
    functionId: params.get("function_id") || ""
  };
}

export default function IdmPage({ settings, auth }: IdmPageProps) {
  const [model, setModel] = useState<OrganizationModel>(EMPTY_MODEL);
  const [assignments, setAssignments] = useState<OrganizationFunctionAssignmentItem[]>([]);
  const [idmSettings, setIdmSettings] = useState<IdmSettings | null>(null);
  const [identityOptions, setIdentityOptions] = useState<IdentityOption[]>([]);
  const [identitySearch, setIdentitySearch] = useState("");
  const [identityLoading, setIdentityLoading] = useState(false);
  const [identityLookupAvailable, setIdentityLookupAvailable] = useState(true);
  const [actingForSearch, setActingForSearch] = useState("");
  const [actingForOptions, setActingForOptions] = useState<IdentityOption[]>([]);
  const [actingForLoading, setActingForLoading] = useState(false);
  const [assignmentDraft, setAssignmentDraft] = useState<AssignmentDraft>(() => emptyAssignmentDraft());
  const [settingsDraft, setSettingsDraft] = useState<SettingsDraft>(() => settingsDraftFrom(null));
  const [editingAssignmentId, setEditingAssignmentId] = useState<string | null>(null);
  const [loading, setLoading] = useState(true);
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState("");
  const [success, setSuccess] = useState("");
  const initialQuery = useMemo(() => idmInitialQuery(), []);
  const appliedInitialQueryRef = useRef(false);

  const canManage = hasScope(auth, "idm:organization_assignment:write") || hasScope(auth, "organizations:function:assign");
  const canSearchIdentities = canManage || hasScope(auth, "idm:organization_identity:read") || hasScope(auth, "admin:users:read");
  const canReadSettings = hasScope(auth, "idm:settings:read") || hasScope(auth, "idm:settings:write") || hasScope(auth, "idm:organization_assignment:read") || hasScope(auth, "idm:organization_assignment:write");
  const canManageSettings = hasScope(auth, "idm:settings:write");
  const unitById = useMemo(() => mapById(model.units), [model.units]);
  const functionById = useMemo(() => mapById(model.functions), [model.functions]);
  const identityOptionById = useMemo(() => mapById(identityOptions), [identityOptions]);
  const actingForOptionById = useMemo(() => mapById(actingForOptions), [actingForOptions]);
  const selectedIdentity = identityOptionById.get(assignmentDraft.identity_id);

  const identitySelectOptions = useMemo(() => {
    if (!assignmentDraft.identity_id || identityOptionById.has(assignmentDraft.identity_id)) return identityOptions;
    return [
      {
        id: assignmentDraft.identity_id,
        display_name: assignmentDraft.identity_id,
        external_subject: null,
        source: "local",
        primary_account_id: assignmentDraft.account_id || null,
        account_ids: assignmentDraft.account_id ? [assignmentDraft.account_id] : [],
        status: "active"
      },
      ...identityOptions
    ];
  }, [assignmentDraft.account_id, assignmentDraft.identity_id, identityOptionById, identityOptions]);

  const accountIds = useMemo(() => {
    const values = new Set(selectedIdentity?.account_ids ?? []);
    if (assignmentDraft.account_id) values.add(assignmentDraft.account_id);
    return Array.from(values).sort((left, right) => left.localeCompare(right));
  }, [assignmentDraft.account_id, selectedIdentity]);
  const assignmentOptions = useMemo(
    () => assignments.filter((item) => item.id !== editingAssignmentId && (!assignmentDraft.function_id || item.function_id === assignmentDraft.function_id)),
    [assignmentDraft.function_id, assignments, editingAssignmentId]
  );
  const assignmentById = useMemo(() => mapById(assignments), [assignments]);
  const sourceAssignment = assignmentDraft.delegated_from_assignment_id ? assignmentById.get(assignmentDraft.delegated_from_assignment_id) : undefined;
  const actingForAccountIds = useMemo(() => {
    const values = new Set<string>();
    if (sourceAssignment?.account_id) values.add(sourceAssignment.account_id);
    const sourceIdentity = sourceAssignment ? identityOptionById.get(sourceAssignment.identity_id) ?? actingForOptionById.get(sourceAssignment.identity_id) : undefined;
    for (const accountId of sourceIdentity?.account_ids ?? []) values.add(accountId);
    for (const identity of actingForOptions) {
      for (const accountId of identity.account_ids) values.add(accountId);
    }
    if (assignmentDraft.acting_for_account_id) values.add(assignmentDraft.acting_for_account_id);
    return Array.from(values).sort((left, right) => left.localeCompare(right));
  }, [actingForOptionById, actingForOptions, assignmentDraft.acting_for_account_id, identityOptionById, sourceAssignment]);
  const initialFunctionFilter = initialQuery.functionId;
  const hasDirtyAssignmentDraft = isAssignmentDirty(assignmentDraft);
  const hasDirtySettingsDraft = canReadSettings && isSettingsDirty(settingsDraft, idmSettings);
  const hasDirtyDraft = hasDirtyAssignmentDraft || hasDirtySettingsDraft;

  const loadData = useCallback(async () => {
    setLoading(true);
    setError("");
    try {
      const [nextModel, nextAssignments, nextSettings] = await Promise.all([
        getOrganizationModel(settings),
        getOrganizationFunctionAssignments(settings),
        canReadSettings ? getIdmSettings(settings).catch(() => null) : Promise.resolve(null)
      ]);
      setModel(nextModel);
      setAssignments(nextAssignments.assignments);
      setIdmSettings(nextSettings);
      if (nextSettings) setSettingsDraft(settingsDraftFrom(nextSettings));
      if (!appliedInitialQueryRef.current && initialQuery.assignmentId) {
        const initialAssignment = nextAssignments.assignments.find((item) => item.id === initialQuery.assignmentId);
        if (initialAssignment) {
          appliedInitialQueryRef.current = true;
          setEditingAssignmentId(initialAssignment.id);
          setAssignmentDraft(assignmentDraftFrom(initialAssignment));
        }
      }
      if (!appliedInitialQueryRef.current && initialFunctionFilter && nextModel.functions.some((item) => item.id === initialFunctionFilter)) {
        appliedInitialQueryRef.current = true;
        setAssignmentDraft((draft) => draft.function_id ? draft : { ...draft, function_id: initialFunctionFilter });
      }
      if (canSearchIdentities) {
        try {
          const identities = await searchOrganizationIdentityOptions(settings, "", 100);
          setIdentityOptions(identities.identities);
          setIdentityLookupAvailable(true);
        } catch {
          setIdentityLookupAvailable(false);
        }
      } else {
        setIdentityLookupAvailable(false);
      }
    } catch (caught) {
      setError(apiErrorMessage(caught));
    } finally {
      setLoading(false);
    }
  }, [canReadSettings, canSearchIdentities, initialFunctionFilter, initialQuery.assignmentId, settings]);

  useEffect(() => {
    void loadData();
  }, [loadData]);

  useEffect(() => {
    if (!canSearchIdentities) return;
    let disposed = false;
    setIdentityLoading(true);
    searchOrganizationIdentityOptions(settings, identitySearch, 50)
      .then((payload) => {
        if (!disposed) {
          setIdentityOptions(payload.identities);
          setIdentityLookupAvailable(true);
        }
      })
      .catch(() => {
        if (!disposed) setIdentityLookupAvailable(false);
      })
      .finally(() => {
        if (!disposed) setIdentityLoading(false);
      });
    return () => {
      disposed = true;
    };
  }, [canSearchIdentities, identitySearch, settings]);

  useEffect(() => {
    if (!canSearchIdentities || assignmentDraft.source !== "acting_for") return;
    let disposed = false;
    setActingForLoading(true);
    searchOrganizationIdentityOptions(settings, actingForSearch, 50)
      .then((payload) => {
        if (!disposed) setActingForOptions(payload.identities);
      })
      .catch(() => {
        if (!disposed) setActingForOptions([]);
      })
      .finally(() => {
        if (!disposed) setActingForLoading(false);
      });
    return () => {
      disposed = true;
    };
  }, [actingForSearch, assignmentDraft.source, canSearchIdentities, settings]);

  const runAction = useCallback(async (action: () => Promise<unknown>, successMessage = ""): Promise<boolean> => {
    setBusy(true);
    setError("");
    setSuccess("");
    try {
      await action();
      if (successMessage) setSuccess(successMessage);
      await loadData();
      return true;
    } catch (caught) {
      setError(apiErrorMessage(caught));
      return false;
    } finally {
      setBusy(false);
    }
  }, [loadData]);

  const discardAssignmentDraft = useCallback(() => {
    setAssignmentDraft(emptyAssignmentDraft());
    setEditingAssignmentId(null);
  }, []);

  const discardDrafts = useCallback(() => {
    discardAssignmentDraft();
    setSettingsDraft(settingsDraftFrom(idmSettings));
  }, [discardAssignmentDraft, idmSettings]);

  const submitSettings = useCallback(async (): Promise<boolean> => {
    if (!hasDirtySettingsDraft) return true;
    if (!canManageSettings) {
      setError("i18n:govoplan-idm.settings_write_permission_required.37f37efa");
      return false;
    }
    const ok = await runAction(
      () => patchIdmSettings(settings, settingsPayload(settingsDraft, idmSettings)),
      "i18n:govoplan-idm.settings_updated.9bbd0867"
    );
    return ok;
  }, [canManageSettings, hasDirtySettingsDraft, idmSettings, runAction, settings, settingsDraft]);

  const submitAssignment = useCallback(async (event?: FormEvent): Promise<boolean> => {
    event?.preventDefault();
    if (!canManage) {
      setError("i18n:govoplan-idm.write_permission_required.c7dde7c6");
      return false;
    }
    if (!assignmentDraft.identity_id) {
      setError("i18n:govoplan-idm.identity_is_required.6ad4ee23");
      return false;
    }
    if (!assignmentDraft.function_id) {
      setError("i18n:govoplan-idm.function_is_required.5cce5b41");
      return false;
    }
    const payload = assignmentPayload(assignmentDraft);
    const ok = await runAction(
      () => editingAssignmentId ? patchOrganizationFunctionAssignment(settings, editingAssignmentId, payload) : createOrganizationFunctionAssignment(settings, payload),
      editingAssignmentId ? "i18n:govoplan-idm.assignment_updated.fbbf9bd6" : "i18n:govoplan-idm.assignment_added.91f1ee42"
    );
    if (ok) discardAssignmentDraft();
    return ok;
  }, [assignmentDraft, canManage, discardAssignmentDraft, editingAssignmentId, runAction, settings]);

  const saveDrafts = useCallback(async (): Promise<boolean> => {
    if (hasDirtyAssignmentDraft && !(await submitAssignment())) return false;
    if (hasDirtySettingsDraft && !(await submitSettings())) return false;
    return true;
  }, [hasDirtyAssignmentDraft, hasDirtySettingsDraft, submitAssignment, submitSettings]);

  useUnsavedDraftGuard({
    dirty: hasDirtyDraft,
    onSave: saveDrafts,
    onDiscard: discardDrafts,
    title: "i18n:govoplan-core.unsaved_changes.29267269",
    message: "i18n:govoplan-core.this_page_has_unsaved_changes_save_them_before_l.419a9d8b"
  });

  const editAssignment = useCallback((item: OrganizationFunctionAssignmentItem) => {
    setEditingAssignmentId(item.id);
    setAssignmentDraft(assignmentDraftFrom(item));
  }, []);

  const toggleAssignment = useCallback((item: OrganizationFunctionAssignmentItem) => {
    void runAction(() => patchOrganizationFunctionAssignment(settings, item.id, { is_active: !item.is_active }));
  }, [runAction, settings]);

  function onIdentityChange(identityId: string) {
    const identity = identityOptionById.get(identityId);
    setAssignmentDraft({
      ...assignmentDraft,
      identity_id: identityId,
      account_id: identity?.primary_account_id ?? identity?.account_ids[0] ?? ""
    });
  }

  function onSourceChange(source: string) {
    setAssignmentDraft({
      ...assignmentDraft,
      source,
      delegated_from_assignment_id: source === "delegated" || source === "acting_for" ? assignmentDraft.delegated_from_assignment_id : "",
      acting_for_account_id: source === "acting_for" ? assignmentDraft.acting_for_account_id : ""
    });
  }

  const assignmentColumns: DataGridColumn<OrganizationFunctionAssignmentItem>[] = [
    {
      id: "identity",
      header: "i18n:govoplan-idm.identity.544a8347",
      minWidth: 220,
      sortable: true,
      filterable: true,
      filterValue: (row) => identityOptionById.get(row.identity_id)?.display_name ?? row.identity_id,
      render: (row) => identityDisplay(identityOptionById.get(row.identity_id), row.identity_id)
    },
    {
      id: "account",
      header: "i18n:govoplan-idm.account.2b2936f8",
      minWidth: 180,
      value: (row) => row.account_id ?? "",
      render: (row) => row.account_id ? <span className="idm-id">{row.account_id}</span> : <span className="idm-empty">i18n:govoplan-idm.none.2baf5c66</span>
    },
    {
      id: "function",
      header: "i18n:govoplan-idm.function.e67d5aba",
      minWidth: 220,
      sortable: true,
      filterable: true,
      filterValue: (row) => `${functionById.get(row.function_id)?.name ?? ""} ${row.function_id}`.trim(),
      render: (row) => {
        const item = functionById.get(row.function_id);
        return item ? functionLabel(item, unitById) : <span className="idm-id">{row.function_id}</span>;
      }
    },
    {
      id: "unit",
      header: "i18n:govoplan-idm.unit.a94c2fbd",
      minWidth: 180,
      render: (row) => unitById.get(row.organization_unit_id)?.name ?? <span className="idm-id">{row.organization_unit_id}</span>
    },
    {
      id: "subunits",
      header: "i18n:govoplan-idm.applies_to_subunits.2e31b50b",
      width: 150,
      render: (row) => activeStatus(row.applies_to_subunits)
    },
    {
      id: "source",
      header: "i18n:govoplan-idm.source.d15b50c9",
      width: 130,
      value: (row) => row.source,
      render: (row) => sourceLabel(row.source)
    },
    {
      id: "status",
      header: "i18n:govoplan-idm.status.9acb445d",
      width: 120,
      render: (row) => activeStatus(row.is_active)
    },
    {
      id: "actions",
      header: "",
      width: 220,
      sticky: "end",
      render: (row) => (
        <div className="idm-row-actions">
          <Button type="button" variant="ghost" disabled={!canManage || busy} onClick={() => editAssignment(row)} title="i18n:govoplan-idm.edit.a5a0f3cc">
            <Edit3 size={16} aria-hidden="true" /> i18n:govoplan-idm.edit.a5a0f3cc
          </Button>
          <Button type="button" variant={row.is_active ? "secondary" : "primary"} disabled={!canManage || busy} onClick={() => toggleAssignment(row)}>
            {row.is_active ? "i18n:govoplan-idm.deactivate.585777c8" : "i18n:govoplan-idm.reactivate.e4871a43"}
          </Button>
        </div>
      )
    }
  ];

  return (
    <div className="content-pad idm-page">
      <div className="page-heading split idm-heading">
        <div>
          <PageTitle loading={loading}>i18n:govoplan-idm.idm.61f4a7a2</PageTitle>
          <p>i18n:govoplan-idm.identity_links_intro.45fed9dd</p>
        </div>
        <div className="idm-toolbar">
          <Button type="button" onClick={() => void loadData()} disabled={loading || busy} title="i18n:govoplan-idm.reload.870ca3ec">
            <RefreshCw size={16} aria-hidden="true" /> i18n:govoplan-idm.reload.870ca3ec
          </Button>
          {hasDirtyDraft && (
            <>
              <Button type="button" onClick={() => void saveDrafts()} disabled={busy} title="i18n:govoplan-idm.save_drafts.32a0d60a">
                <Save size={16} aria-hidden="true" /> i18n:govoplan-idm.save_drafts.32a0d60a
              </Button>
              <Button type="button" variant="ghost" onClick={discardDrafts} disabled={busy} title="i18n:govoplan-idm.discard_drafts.c0a86816">
                <XCircle size={16} aria-hidden="true" /> i18n:govoplan-idm.discard_drafts.c0a86816
              </Button>
            </>
          )}
        </div>
      </div>

      {error && <DismissibleAlert tone="danger" resetKey={error} floating>{error}</DismissibleAlert>}
      {success && !error && <DismissibleAlert tone="success" resetKey={success} floating>{success}</DismissibleAlert>}
      {!canManage && <DismissibleAlert tone="warning" dismissible={false}>i18n:govoplan-idm.write_permission_required.c7dde7c6</DismissibleAlert>}

      <LoadingFrame loading={loading || busy} label="i18n:govoplan-idm.loading_idm_assignments.0b1501bd">
        <div className="idm-layout">
          <div className="idm-editor-stack">
            <Card title="i18n:govoplan-idm.assignment_editor.e20598e7">
              <form className="idm-form-grid" onSubmit={(event) => void submitAssignment(event)}>
              <FormField label="i18n:govoplan-idm.identity_search.d3460fcf">
                <input
                  value={identitySearch}
                  placeholder="i18n:govoplan-idm.search_identities.88a9ef15"
                  disabled={!canSearchIdentities || busy}
                  onChange={(event) => setIdentitySearch(event.target.value)}
                />
              </FormField>
              <FormField label="i18n:govoplan-idm.select_identity.91d31615">
                <select value={assignmentDraft.identity_id} disabled={!canManage || busy} onChange={(event) => onIdentityChange(event.target.value)}>
                  <option value="">i18n:govoplan-idm.select_identity.91d31615</option>
                  {identitySelectOptions.map((item) => (
                    <option key={item.id} value={item.id}>{item.display_name || item.external_subject || item.id}</option>
                  ))}
                </select>
              </FormField>
              <FormField label="i18n:govoplan-idm.account.2b2936f8">
                <select value={assignmentDraft.account_id} disabled={!canManage || busy} onChange={(event) => setAssignmentDraft({ ...assignmentDraft, account_id: event.target.value })}>
                  <option value="">i18n:govoplan-idm.none.2baf5c66</option>
                  {accountIds.map((accountId) => <option key={accountId} value={accountId}>{accountId}</option>)}
                </select>
              </FormField>
              <FormField label="i18n:govoplan-idm.select_function.2bec86e0">
                <select value={assignmentDraft.function_id} disabled={!canManage || busy} onChange={(event) => setAssignmentDraft({ ...assignmentDraft, function_id: event.target.value })}>
                  <option value="">i18n:govoplan-idm.select_function.2bec86e0</option>
                  {model.functions.map((item) => <option key={item.id} value={item.id}>{functionLabel(item, unitById)}</option>)}
                </select>
              </FormField>
              <FormField label="i18n:govoplan-idm.source.d15b50c9">
                <select value={assignmentDraft.source} disabled={!canManage || busy} onChange={(event) => onSourceChange(event.target.value)}>
                  {SOURCE_OPTIONS.map((item) => <option key={item.value} value={item.value}>{item.label}</option>)}
                </select>
              </FormField>
              {(assignmentDraft.source === "delegated" || assignmentDraft.source === "acting_for") && (
                <FormField label="i18n:govoplan-idm.delegated_from_assignment_id.20d4a548">
                  <select value={assignmentDraft.delegated_from_assignment_id} disabled={!canManage || busy} onChange={(event) => setAssignmentDraft({ ...assignmentDraft, delegated_from_assignment_id: event.target.value, acting_for_account_id: "" })}>
                    <option value="">i18n:govoplan-idm.none.2baf5c66</option>
                    {assignmentOptions.map((item) => (
                      <option key={item.id} value={item.id}>{assignmentOptionLabel(item, identityOptionById, functionById, unitById)}</option>
                    ))}
                  </select>
                </FormField>
              )}
              {assignmentDraft.source === "acting_for" && (
                <>
                  <FormField label="i18n:govoplan-idm.acting_for_search.b7c526c7">
                    <input
                      value={actingForSearch}
                      placeholder="i18n:govoplan-idm.search_identities.88a9ef15"
                      disabled={!canSearchIdentities || busy}
                      onChange={(event) => setActingForSearch(event.target.value)}
                    />
                  </FormField>
                  <FormField label="i18n:govoplan-idm.acting_for_account_id.5d7ade5b">
                    <select value={assignmentDraft.acting_for_account_id} disabled={!canManage || busy} onChange={(event) => setAssignmentDraft({ ...assignmentDraft, acting_for_account_id: event.target.value })}>
                      <option value="">i18n:govoplan-idm.select_account.982ee1ad</option>
                      {actingForAccountIds.map((accountId) => <option key={accountId} value={accountId}>{accountId}</option>)}
                    </select>
                  </FormField>
                </>
              )}
              <div className="idm-check-list">
                <label>
                  <input type="checkbox" checked={assignmentDraft.applies_to_subunits} disabled={!canManage || busy} onChange={(event) => setAssignmentDraft({ ...assignmentDraft, applies_to_subunits: event.target.checked })} />
                  <span>i18n:govoplan-idm.applies_to_subunits.2e31b50b</span>
                </label>
                <label>
                  <input type="checkbox" checked={assignmentDraft.is_active} disabled={!canManage || busy} onChange={(event) => setAssignmentDraft({ ...assignmentDraft, is_active: event.target.checked })} />
                  <span>i18n:govoplan-idm.active.7bd0e9f8</span>
                </label>
              </div>
              {!identityLookupAvailable && <p className="idm-muted wide">i18n:govoplan-idm.identity_lookup_unavailable.b76f7714</p>}
              {identityLoading && <p className="idm-muted wide">i18n:govoplan-idm.loading_identities.f3b84693</p>}
              {actingForLoading && <p className="idm-muted wide">i18n:govoplan-idm.loading_acting_for_accounts.c9894b1e</p>}
              {!model.functions.length && <p className="idm-muted wide">i18n:govoplan-idm.no_functions_available.51ba08eb</p>}
              <div className="idm-form-actions wide">
                <Button type="submit" variant="primary" disabled={!canManage || busy}>
                  {editingAssignmentId ? "i18n:govoplan-idm.update_assignment.e20f52aa" : "i18n:govoplan-idm.add_assignment.08f2a0d5"}
                </Button>
                {editingAssignmentId && (
                  <Button type="button" variant="ghost" onClick={discardAssignmentDraft} disabled={busy}>i18n:govoplan-idm.cancel_edit.ea4781e0</Button>
                )}
              </div>
              </form>
            </Card>

            {canReadSettings && (
              <Card title="i18n:govoplan-idm.idm_governance.6e4f3251">
                <form className="idm-form-grid" onSubmit={(event) => { event.preventDefault(); void submitSettings(); }}>
                <div className="idm-check-list wide">
                  <label>
                    <input
                      type="checkbox"
                      checked={settingsDraft.require_assignment_change_requests}
                      disabled={!canManageSettings || busy}
                      onChange={(event) => setSettingsDraft({ ...settingsDraft, require_assignment_change_requests: event.target.checked })}
                    />
                    <span>i18n:govoplan-idm.require_assignment_change_requests.697718a1</span>
                  </label>
                </div>
                <FormField label="i18n:govoplan-idm.audit_detail_level.eb2e6fd2">
                  <select
                    value={settingsDraft.audit_detail_level}
                    disabled={!canManageSettings || busy}
                    onChange={(event) => setSettingsDraft({ ...settingsDraft, audit_detail_level: event.target.value as SettingsDraft["audit_detail_level"] })}
                  >
                    <option value="summary">i18n:govoplan-idm.summary.f1c5b7ab</option>
                    <option value="standard">i18n:govoplan-idm.standard.6edc51aa</option>
                    <option value="full">i18n:govoplan-idm.full.7f021a14</option>
                  </select>
                </FormField>
                <FormField label="i18n:govoplan-idm.change_retention_days.4a91f7d3">
                  <input
                    type="number"
                    min="0"
                    value={settingsDraft.change_retention_days}
                    disabled={!canManageSettings || busy}
                    onChange={(event) => setSettingsDraft({ ...settingsDraft, change_retention_days: event.target.value })}
                  />
                </FormField>
                <div className="idm-form-actions wide">
                  <Button type="submit" variant="primary" disabled={!canManageSettings || busy || !hasDirtySettingsDraft}>
                    i18n:govoplan-idm.save_settings.4602c430
                  </Button>
                </div>
                </form>
              </Card>
            )}
          </div>

          <Card title="i18n:govoplan-idm.assignments.a0d19ec5">
            <DataGrid
              id="idm-organization-function-assignments"
              rows={assignments}
              columns={assignmentColumns}
              getRowKey={(row) => row.id}
              emptyText="i18n:govoplan-idm.no_assignments.41193ce8"
              initialFilters={initialFunctionFilter ? { function: initialFunctionFilter } : undefined}
              initialFit="container"
            />
          </Card>
        </div>
      </LoadingFrame>
    </div>
  );
}
