import { useEffect, useMemo, useRef, useState } from "react";
import { Pencil, Plus, Trash2 } from "lucide-react";
import type { ApiSettings, AuthInfo, OrganizationFunctionPickerUiCapability, OrganizationFunctionSelection } from "@govoplan/core-webui";
import {
  createExternalFunctionRoleMapping,
  deleteExternalFunctionRoleMapping,
  fetchExternalFunctionRoleMappingsDelta,
  fetchRolesDelta,
  updateExternalFunctionRoleMapping,
  type ExternalFunctionRoleMappingItem,
  type RoleSummary
} from "../../api/admin";
import { Button } from "@govoplan/core-webui";
import { DataGrid, type DataGridColumn } from "@govoplan/core-webui";
import { Dialog } from "@govoplan/core-webui";
import { FormField } from "@govoplan/core-webui";
import { ConfirmDialog } from "@govoplan/core-webui";
import { AdminIconButton, AdminPageLayout, adminErrorMessage, formatAdminDateTime as formatDateTime } from "@govoplan/core-webui";
import { i18nMessage, useDeltaWatermarks, useUnsavedDraftGuard } from "@govoplan/core-webui";
import { loadDeltaRows } from "./utils/deltaRows";

const emptyDraft = {
  sourceModule: "organizations",
  functionId: "",
  functionLabel: "",
  organizationUnitLabel: "",
  roleId: ""
};

export default function ExternalFunctionRoleMappingsPanel({
  settings,
  auth,
  functionPicker,
  canWrite,
  onAuthRefresh
}: {
  settings: ApiSettings;
  auth: AuthInfo;
  functionPicker: OrganizationFunctionPickerUiCapability;
  canWrite: boolean;
  onAuthRefresh: () => Promise<void>;
}) {
  const [mappings, setMappings] = useState<ExternalFunctionRoleMappingItem[]>([]);
  const [roles, setRoles] = useState<RoleSummary[]>([]);
  const mappingsRef = useRef<ExternalFunctionRoleMappingItem[]>([]);
  const rolesRef = useRef<RoleSummary[]>([]);
  const { getDeltaWatermark, setDeltaWatermark, resetDeltaWatermark } = useDeltaWatermarks();
  const [editing, setEditing] = useState<ExternalFunctionRoleMappingItem | "new" | null>(null);
  const [draft, setDraft] = useState(emptyDraft);
  const [savedDraftKey, setSavedDraftKey] = useState(draftKey(emptyDraft));
  const [deleting, setDeleting] = useState<ExternalFunctionRoleMappingItem | null>(null);
  const [loading, setLoading] = useState(true);
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState("");
  const [success, setSuccess] = useState("");
  const tenantId = (auth.active_tenant ?? auth.tenant).id;
  const dirty = editing !== null && draftKey(draft) !== savedDraftKey;

  useUnsavedDraftGuard({
    dirty,
    onSave: save,
    onDiscard: closeEditor
  });

  async function load() {
    setLoading(true);
    setError("");
    try {
      const [nextMappings, nextRoles] = await Promise.all([
        loadDeltaRows(
          mappingsRef.current,
          "access:external-function-role-mappings",
          getDeltaWatermark,
          setDeltaWatermark,
          (since) => fetchExternalFunctionRoleMappingsDelta(settings, { since }),
          (response) => response.mappings,
          (mapping) => mapping.id,
          "access_external_function_role_mapping",
          sortMappings
        ),
        loadDeltaRows(
          rolesRef.current,
          "access:roles-for-external-function-mappings",
          getDeltaWatermark,
          setDeltaWatermark,
          (since) => fetchRolesDelta(settings, { since }),
          (response) => response.roles,
          (role) => role.id,
          "access_role",
          sortRoles
        )
      ]);
      mappingsRef.current = nextMappings;
      rolesRef.current = nextRoles;
      setMappings(nextMappings);
      setRoles(nextRoles.filter((role) => role.level === "tenant"));
    } catch (err) {
      setError(adminErrorMessage(err));
    } finally {
      setLoading(false);
    }
  }

  useEffect(() => {
    mappingsRef.current = [];
    rolesRef.current = [];
    resetDeltaWatermark();
    void load();
  }, [settings.accessToken, settings.apiBaseUrl, tenantId, resetDeltaWatermark]);

  const roleById = useMemo(() => new Map(roles.map((role) => [role.id, role])), [roles]);
  const assignableRoles = useMemo(() => roles.filter((role) => role.is_assignable), [roles]);

  function openCreate() {
    const initial = { ...emptyDraft, roleId: assignableRoles[0]?.id ?? "" };
    setDraft(initial);
    setSavedDraftKey(draftKey(initial));
    setEditing("new");
    setError("");
  }

  function openEdit(mapping: ExternalFunctionRoleMappingItem) {
    const nextDraft = {
      sourceModule: mapping.source_module,
      functionId: mapping.function_id,
      functionLabel: "",
      organizationUnitLabel: "",
      roleId: mapping.role_id
    };
    setDraft(nextDraft);
    setSavedDraftKey(draftKey(nextDraft));
    setEditing(mapping);
    setError("");
  }

  function closeEditor() {
    setEditing(null);
    setDraft(emptyDraft);
    setSavedDraftKey(draftKey(emptyDraft));
  }

  async function save(): Promise<boolean> {
    setBusy(true);
    setError("");
    try {
      if (editing === "new") {
        await createExternalFunctionRoleMapping(settings, {
          source_module: functionPicker.sourceModule,
          function_id: draft.functionId.trim(),
          role_id: draft.roleId
        });
        setSuccess(i18nMessage("i18n:govoplan-access.function_role_mapping_created.7a25eb5a"));
      } else if (editing) {
        await updateExternalFunctionRoleMapping(settings, editing.id, { role_id: draft.roleId });
        setSuccess(i18nMessage("i18n:govoplan-access.function_role_mapping_updated.76020443"));
      }
      closeEditor();
      await onAuthRefresh();
      mappingsRef.current = [];
      resetDeltaWatermark("access:external-function-role-mappings");
      await load();
      return true;
    } catch (err) {
      setError(adminErrorMessage(err));
      return false;
    } finally {
      setBusy(false);
    }
  }

  async function remove() {
    if (!deleting) return;
    setBusy(true);
    setError("");
    try {
      await deleteExternalFunctionRoleMapping(settings, deleting.id);
      setSuccess(i18nMessage("i18n:govoplan-access.function_role_mapping_deleted.fb180786"));
      setDeleting(null);
      await onAuthRefresh();
      mappingsRef.current = [];
      resetDeltaWatermark("access:external-function-role-mappings");
      await load();
    } catch (err) {
      setError(adminErrorMessage(err));
    } finally {
      setBusy(false);
    }
  }

  const columns = useMemo<DataGridColumn<ExternalFunctionRoleMappingItem>[]>(
    () => [
      {
        id: "function",
        header: "i18n:govoplan-access.function_fact.4f7435e4",
        width: "minmax(260px, 1fr)",
        minWidth: 220,
        resizable: true,
        sticky: "start",
        sortable: true,
        filterable: true,
        value: (row) => `${row.source_module} ${row.function_id}`,
        render: (row) => (
          <>
            {functionPicker.renderLabel?.({
              settings,
              auth,
              sourceModule: row.source_module,
              functionId: row.function_id,
              fallback: (
                <div>
                  <strong>{row.source_module}</strong>
                  <div className="muted small-note">{row.function_id}</div>
                </div>
              )
            }) ?? (
              <div>
                <strong>{row.source_module}</strong>
                <div className="muted small-note">{row.function_id}</div>
              </div>
            )}
          </>
        )
      },
      {
        id: "role",
        header: "i18n:govoplan-access.role.c3f104d1",
        width: "minmax(220px, 1fr)",
        minWidth: 190,
        resizable: true,
        sortable: true,
        filterable: true,
        value: (row) => roleById.get(row.role_id)?.name ?? row.role_id,
        render: (row) => {
          const role = roleById.get(row.role_id);
          return (
            <div>
              <strong>{role?.name ?? row.role_id}</strong>
              {role && <div className="muted small-note">{role.slug}</div>}
            </div>
          );
        }
      },
      {
        id: "updated",
        header: "i18n:govoplan-access.updated.f2f8570d",
        width: 180,
        minWidth: 150,
        resizable: true,
        sortable: true,
        value: (row) => row.updated_at,
        render: (row) => formatDateTime(row.updated_at)
      },
      {
        id: "actions",
        header: "i18n:govoplan-access.actions.c3cd636a",
        width: 130,
        sticky: "end",
        resizable: false,
        align: "right",
        render: (row) => (
          <div className="admin-icon-actions">
            <AdminIconButton label={i18nMessage("i18n:govoplan-access.edit_value.fad75899", { value0: row.function_id })} icon={<Pencil />} onClick={() => openEdit(row)} disabled={!canWrite} />
            <AdminIconButton label={i18nMessage("i18n:govoplan-access.delete_value.4d18989e", { value0: row.function_id })} icon={<Trash2 />} variant="danger" onClick={() => setDeleting(row)} disabled={!canWrite} />
          </div>
        )
      }
    ],
    [auth, canWrite, functionPicker, roleById, settings]
  );

  return (
    <>
      <AdminPageLayout
        title="i18n:govoplan-access.function_role_mappings.2b64e9c3"
        description="i18n:govoplan-access.map_accepted_function_facts_to_tenant_roles_.7581e5cf"
        loading={loading}
        error={error}
        success={success}
        actions={
          <>
            <Button onClick={() => void load()} disabled={loading}>i18n:govoplan-access.reload.cce71553</Button>
            <AdminIconButton label="i18n:govoplan-access.add_function_role_mapping.1bc376ac" icon={<Plus />} variant="primary" onClick={openCreate} disabled={!canWrite || !assignableRoles.length} />
          </>
        }
      >
        <div className="admin-table-surface">
          <DataGrid
            id="admin-external-function-role-mappings-v1"
            rows={mappings}
            columns={columns}
            initialFit="container"
            getRowKey={(row) => row.id}
            emptyText="i18n:govoplan-access.no_function_role_mappings_found.f735ff54"
          />
        </div>
      </AdminPageLayout>

      <Dialog
        open={editing !== null}
        title={editing === "new" ? "i18n:govoplan-access.create_function_role_mapping.3718168d" : "i18n:govoplan-access.edit_function_role_mapping.91ee75af"}
        onClose={() => !busy && closeEditor()}
        className="admin-dialog"
        footer={
          <>
            <Button onClick={closeEditor} disabled={busy}>i18n:govoplan-access.cancel.77dfd213</Button>
            <Button variant="primary" onClick={() => void save()} disabled={!canWrite || busy || !draft.functionId.trim() || !draft.roleId}>
              {busy ? "i18n:govoplan-access.saving.56a2285c" : "i18n:govoplan-access.save_mapping.a4ac90e9"}
            </Button>
          </>
        }
      >
        <div className="admin-form-grid">
          <FormField label="i18n:govoplan-access.function_id.e5e08937">
            {functionPicker.renderPicker({
              settings,
              auth,
              disabled: editing !== "new",
              value: draft.functionId ? draftSelection(draft) : null,
              onChange: (selection) => setDraft({ ...draft, ...selectionDraft(selection) })
            })}
          </FormField>
          <FormField label="i18n:govoplan-access.role.c3f104d1">
            <select value={draft.roleId} onChange={(event) => setDraft({ ...draft, roleId: event.target.value })}>
              <option value="">i18n:govoplan-access.select_role.c543f191</option>
              {assignableRoles.map((role) => (
                <option key={role.id} value={role.id}>{role.name} ({role.slug})</option>
              ))}
            </select>
          </FormField>
        </div>
        <p className="muted small-note">i18n:govoplan-access.function_role_mapping_help.0cf9996a</p>
      </Dialog>

      <ConfirmDialog
        open={Boolean(deleting)}
        title="i18n:govoplan-access.delete_function_role_mapping.0c0eec6e"
        message={i18nMessage("i18n:govoplan-access.delete_function_role_mapping_value.419da2aa", { value0: deleting?.function_id })}
        confirmLabel="i18n:govoplan-access.delete_mapping.0d27d92a"
        tone="danger"
        busy={busy}
        onCancel={() => setDeleting(null)}
        onConfirm={() => void remove()}
      />
    </>
  );
}

function draftKey(draft: typeof emptyDraft): string {
  return JSON.stringify(draft);
}

function draftSelection(draft: typeof emptyDraft): OrganizationFunctionSelection {
  return {
    sourceModule: "organizations",
    functionId: draft.functionId,
    label: draft.functionLabel || null,
    organizationUnitLabel: draft.organizationUnitLabel || null
  };
}

function selectionDraft(selection: OrganizationFunctionSelection | null): Pick<typeof emptyDraft, "sourceModule" | "functionId" | "functionLabel" | "organizationUnitLabel"> {
  return {
    sourceModule: selection?.sourceModule ?? "organizations",
    functionId: selection?.functionId ?? "",
    functionLabel: selection?.label ?? "",
    organizationUnitLabel: selection?.organizationUnitLabel ?? ""
  };
}

function sortMappings(left: ExternalFunctionRoleMappingItem, right: ExternalFunctionRoleMappingItem): number {
  const sourceDelta = left.source_module.localeCompare(right.source_module);
  return sourceDelta !== 0 ? sourceDelta : left.function_id.localeCompare(right.function_id);
}

function sortRoles(left: RoleSummary, right: RoleSummary): number {
  return left.name.localeCompare(right.name);
}
