import { useEffect, useMemo, useState } from "react";
import { KeyRound, Pencil, Plus, RefreshCw, Trash2 } from "lucide-react";
import type {
  ApiSettings,
  CredentialEnvelopeSummary,
  MailProfileScope
} from "../types";
import {
  createCredentialEnvelope,
  deleteCredentialEnvelope,
  listCredentialEnvelopes,
  updateCredentialEnvelope
} from "../api/credentials";
import Button from "./Button";
import Card from "./Card";
import ConfirmDialog from "./ConfirmDialog";
import ConnectionTree, { type ConnectionTreeColumn } from "./ConnectionTree";
import Dialog from "./Dialog";
import DismissibleAlert from "./DismissibleAlert";
import FormField from "./FormField";
import LoadingFrame from "./LoadingFrame";
import PasswordField from "./PasswordField";
import StatusBadge from "./StatusBadge";
import TableActionGroup from "./table/TableActionGroup";
import ToggleSwitch from "./ToggleSwitch";
import { useUnsavedDraftGuard } from "./UnsavedChangesGuard";

export type CredentialEnvelopeTargetOption = {
  id: string;
  label: string;
  secondary?: string | null;
};

export type CredentialEnvelopeManagerProps = {
  settings: ApiSettings;
  scopeType: Extract<MailProfileScope, "system" | "tenant" | "user" | "group">;
  scopeId?: string | null;
  targetOptions?: CredentialEnvelopeTargetOption[];
  targetLabel?: string;
  title?: string;
  canWrite: boolean;
};

type CredentialKind = "username_password" | "token" | "api_key";

type CredentialDraft = {
  name: string;
  description: string;
  credentialKind: CredentialKind;
  username: string;
  secret: string;
  allowedModules: string;
  allowedServerRefs: string;
  inheritToLowerScopes: boolean;
  isActive: boolean;
  clearSecret: boolean;
  retainedPublicData: Record<string, unknown>;
};

const EMPTY_DRAFT: CredentialDraft = {
  name: "",
  description: "",
  credentialKind: "username_password",
  username: "",
  secret: "",
  allowedModules: "",
  allowedServerRefs: "",
  inheritToLowerScopes: false,
  isActive: true,
  clearSecret: false,
  retainedPublicData: {}
};

export default function CredentialEnvelopeManager({
  settings,
  scopeType,
  scopeId,
  targetOptions = [],
  targetLabel = "Target",
  title = "Credential envelopes",
  canWrite
}: CredentialEnvelopeManagerProps) {
  const [selectedTargetId, setSelectedTargetId] = useState(scopeId ?? targetOptions[0]?.id ?? "");
  const [credentials, setCredentials] = useState<CredentialEnvelopeSummary[]>([]);
  const [loading, setLoading] = useState(false);
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState("");
  const [notice, setNotice] = useState("");
  const [editing, setEditing] = useState<CredentialEnvelopeSummary | "new" | null>(null);
  const [draft, setDraft] = useState<CredentialDraft>(EMPTY_DRAFT);
  const [savedDraftKey, setSavedDraftKey] = useState("");
  const [deleting, setDeleting] = useState<CredentialEnvelopeSummary | null>(null);
  const activeScopeId = scopeType === "system" || scopeType === "tenant"
    ? scopeId ?? null
    : selectedTargetId || null;
  const scopeReady = scopeType === "system" || scopeType === "tenant" || Boolean(activeScopeId);
  const draftDirty = Boolean(editing) && credentialDraftKey(draft) !== savedDraftKey;

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

  useEffect(() => {
    if (scopeId) {
      setSelectedTargetId(scopeId);
      return;
    }
    if (
      targetOptions.length > 0 &&
      !targetOptions.some((option) => option.id === selectedTargetId)
    ) {
      setSelectedTargetId(targetOptions[0].id);
    }
  }, [scopeId, selectedTargetId, targetOptions]);

  useEffect(() => {
    void loadCredentials();
  }, [
    activeScopeId,
    scopeReady,
    scopeType,
    settings.accessToken,
    settings.apiBaseUrl,
    settings.apiKey
  ]);

  async function loadCredentials() {
    if (!scopeReady) {
      setCredentials([]);
      return;
    }
    setLoading(true);
    setError("");
    try {
      setCredentials(
        await listCredentialEnvelopes(settings, {
          scope_type: scopeType,
          scope_id: activeScopeId,
          include_inactive: true
        })
      );
    } catch (err) {
      setCredentials([]);
      setError(errorMessage(err));
    } finally {
      setLoading(false);
    }
  }

  function openCreate() {
    const next = { ...EMPTY_DRAFT, retainedPublicData: {} };
    setEditing("new");
    setDraft(next);
    setSavedDraftKey(credentialDraftKey(next));
    setError("");
    setNotice("");
  }

  function openEdit(credential: CredentialEnvelopeSummary) {
    const next = credentialDraft(credential);
    setEditing(credential);
    setDraft(next);
    setSavedDraftKey(credentialDraftKey(next));
    setError("");
    setNotice("");
  }

  function closeEditor() {
    if (saving) return;
    setEditing(null);
    setDraft(EMPTY_DRAFT);
    setSavedDraftKey("");
  }

  async function saveDraft(): Promise<boolean> {
    if (!editing || !scopeReady || !draft.name.trim() || !canWrite) return false;
    if (editing === "new" && !draft.secret.trim()) {
      setError("Enter a secret before creating the credential.");
      return false;
    }
    const kindChanged = editing !== "new" && draft.credentialKind !== editing.credential_kind;
    if (kindChanged && !draft.secret.trim()) {
      setError("Enter a replacement secret when changing the credential type.");
      return false;
    }
    setSaving(true);
    setError("");
    try {
      const publicData = {
        ...draft.retainedPublicData,
        username: draft.username.trim() || undefined
      };
      if (!draft.username.trim()) delete publicData.username;
      const shared = {
        name: draft.name.trim(),
        description: draft.description.trim() || null,
        credential_kind: draft.credentialKind,
        public_data: publicData,
        allowed_modules: splitValues(draft.allowedModules),
        allowed_server_refs: splitValues(draft.allowedServerRefs),
        inherit_to_lower_scopes: draft.inheritToLowerScopes,
        is_active: draft.isActive
      };
      if (editing === "new") {
        await createCredentialEnvelope(settings, {
          scope_type: scopeType,
          scope_id: activeScopeId,
          ...shared,
          secret_data: secretPayload(draft)
        });
      } else {
        await updateCredentialEnvelope(settings, editing.id, {
          ...shared,
          ...(draft.secret.trim() ? { secret_data: secretPayload(draft) } : {}),
          clear_secret: draft.clearSecret
        });
      }
      setNotice(editing === "new" ? "Credential created." : "Credential saved.");
      closeEditor();
      await loadCredentials();
      return true;
    } catch (err) {
      setError(errorMessage(err));
      return false;
    } finally {
      setSaving(false);
    }
  }

  async function confirmDelete() {
    if (!deleting) return;
    setSaving(true);
    setError("");
    try {
      await deleteCredentialEnvelope(settings, deleting.id);
      setNotice("Credential deleted. Connections using it will remain configured but cannot authenticate.");
      setDeleting(null);
      await loadCredentials();
    } catch (err) {
      setError(errorMessage(err));
    } finally {
      setSaving(false);
    }
  }

  const columns = useMemo<ConnectionTreeColumn<CredentialEnvelopeSummary>[]>(
    () => [
      {
        id: "credential",
        header: "Credential",
        width: "minmax(240px, 1.4fr)",
        render: (credential) => (
          <div className="connection-tree-main">
            <strong>{credential.name}</strong>
            <div className="connection-tree-muted-line">
              {credential.description || credential.public_data.username
                ? String(credential.description || credential.public_data.username)
                : credential.id}
            </div>
          </div>
        )
      },
      {
        id: "kind",
        header: "Type",
        width: "minmax(150px, 0.7fr)",
        render: (credential) => credentialKindLabel(credential.credential_kind)
      },
      {
        id: "availability",
        header: "Availability",
        width: "minmax(220px, 1fr)",
        render: (credential) => availabilityLabel(credential)
      },
      {
        id: "status",
        header: "Status",
        width: "120px",
        render: (credential) => (
          <StatusBadge
            status={credential.is_active && credential.secret_configured ? "success" : "inactive"}
            label={
              credential.is_active
                ? credential.secret_configured
                  ? "Ready"
                  : "No secret"
                : "Inactive"
            }
          />
        )
      }
    ],
    []
  );

  const kindChanged = editing !== null && editing !== "new" &&
    draft.credentialKind !== editing.credential_kind;
  const saveDisabled = saving || !canWrite || !draft.name.trim() ||
    (editing === "new" && !draft.secret.trim()) ||
    (kindChanged && !draft.secret.trim());

  return (
    <div className="credential-envelope-manager">
      {targetOptions.length > 0 && (
        <FormField label={targetLabel}>
          <select
            value={selectedTargetId}
            disabled={saving}
            onChange={(event) => setSelectedTargetId(event.target.value)}
          >
            {targetOptions.map((option) => (
              <option key={option.id} value={option.id}>
                {option.label}{option.secondary ? ` - ${option.secondary}` : ""}
              </option>
            ))}
          </select>
        </FormField>
      )}
      {error && <DismissibleAlert tone="danger" resetKey={error}>{error}</DismissibleAlert>}
      {notice && !error && <DismissibleAlert tone="success" resetKey={notice}>{notice}</DismissibleAlert>}
      <Card
        title={title}
        actions={
          <div className="button-row compact-actions">
            <Button
              type="button"
              title="Reload credentials"
              aria-label="Reload credentials"
              onClick={() => void loadCredentials()}
              disabled={loading}
            >
              <RefreshCw size={16} />
            </Button>
            <Button
              type="button"
              variant="primary"
              onClick={openCreate}
              disabled={!canWrite || !scopeReady || loading}
            >
              <Plus size={16} /> Add credential
            </Button>
          </div>
        }
      >
        <LoadingFrame loading={loading}>
          <ConnectionTree
            rows={credentials}
            columns={columns}
            getRowKey={(credential) => credential.id}
            emptyText="No credentials are configured for this scope."
            renderActions={(credential) => (
              <TableActionGroup
                actions={[
                  {
                    id: "edit",
                    label: `Edit ${credential.name}`,
                    icon: <Pencil size={15} />,
                    onClick: () => openEdit(credential),
                    disabled: !canWrite || saving
                  },
                  {
                    id: "delete",
                    label: `Delete ${credential.name}`,
                    icon: <Trash2 size={15} />,
                    onClick: () => setDeleting(credential),
                    disabled: !canWrite || saving,
                    variant: "danger"
                  }
                ]}
              />
            )}
          />
        </LoadingFrame>
      </Card>

      <Dialog
        open={Boolean(editing)}
        title={editing === "new" ? "Add reusable credential" : "Edit reusable credential"}
        onClose={closeEditor}
        closeDisabled={saving}
        className="admin-dialog admin-dialog-wide adaptive-config-dialog"
        footerClassName="button-row compact-actions"
        footer={
          <>
            <Button onClick={closeEditor} disabled={saving}>Cancel</Button>
            <Button variant="primary" onClick={() => void saveDraft()} disabled={saveDisabled}>
              <KeyRound size={16} /> {saving ? "Saving" : "Save credential"}
            </Button>
          </>
        }
      >
        <div className="adaptive-config-form">
          <section className="adaptive-config-section">
            <header>
              <h3>Identity</h3>
              <p>The name and type are visible; secret values are never returned by the API.</p>
            </header>
            <div className="form-grid two">
              <FormField label="Name">
                <input value={draft.name} disabled={saving} onChange={(event) => setDraft({ ...draft, name: event.target.value })} autoFocus />
              </FormField>
              <FormField label="Type">
                <select value={draft.credentialKind} disabled={saving} onChange={(event) => setDraft({ ...draft, credentialKind: event.target.value as CredentialKind, secret: "", clearSecret: false })}>
                  <option value="username_password">Username and password</option>
                  <option value="token">Access token</option>
                  <option value="api_key">API key</option>
                </select>
              </FormField>
              <FormField label="Description">
                <input value={draft.description} disabled={saving} onChange={(event) => setDraft({ ...draft, description: event.target.value })} />
              </FormField>
              <FormField label={draft.credentialKind === "username_password" ? "Username" : "Account or key label"}>
                <input value={draft.username} disabled={saving} onChange={(event) => setDraft({ ...draft, username: event.target.value })} />
              </FormField>
              <FormField
                label={secretFieldLabel(draft.credentialKind)}
                help={editing !== "new" ? "Leave blank to retain the configured secret." : undefined}
              >
                <PasswordField value={draft.secret} onValueChange={(secret) => setDraft({ ...draft, secret, clearSecret: false })} disabled={saving} autoComplete="new-password" />
              </FormField>
              {editing !== "new" && (
                <ToggleSwitch
                  checked={draft.clearSecret}
                  disabled={saving || Boolean(draft.secret)}
                  onChange={(clearSecret) => setDraft({ ...draft, clearSecret })}
                  label="Remove configured secret"
                />
              )}
            </div>
          </section>
          <section className="adaptive-config-section">
            <header>
              <h3>Availability</h3>
              <p>Empty module or server lists mean every module or server allowed by scope.</p>
            </header>
            <div className="form-grid two">
              <FormField label="Modules" help="Comma-separated module IDs, for example mail, files, calendar, addresses.">
                <input value={draft.allowedModules} disabled={saving} onChange={(event) => setDraft({ ...draft, allowedModules: event.target.value })} />
              </FormField>
              <FormField label="Servers" help="Optional references such as mail:server-id, files:connection-id, calendar:source-id, or addresses:source-id.">
                <input value={draft.allowedServerRefs} disabled={saving} onChange={(event) => setDraft({ ...draft, allowedServerRefs: event.target.value })} />
              </FormField>
              <ToggleSwitch checked={draft.inheritToLowerScopes} disabled={saving} onChange={(inheritToLowerScopes) => setDraft({ ...draft, inheritToLowerScopes })} label="Visible to lower scopes" />
              <ToggleSwitch checked={draft.isActive} disabled={saving} onChange={(isActive) => setDraft({ ...draft, isActive })} label="Active" />
            </div>
          </section>
        </div>
      </Dialog>

      <ConfirmDialog
        open={Boolean(deleting)}
        title="Delete credential"
        message={`Delete ${deleting?.name ?? "this credential"}? Connections that reference it will stop authenticating; the secret cannot be recovered.`}
        confirmLabel="Delete"
        tone="danger"
        busy={saving}
        onCancel={() => setDeleting(null)}
        onConfirm={() => void confirmDelete()}
      />
    </div>
  );
}

function credentialDraft(credential: CredentialEnvelopeSummary): CredentialDraft {
  return {
    ...EMPTY_DRAFT,
    name: credential.name,
    description: credential.description ?? "",
    credentialKind: supportedCredentialKind(credential.credential_kind),
    username: String(credential.public_data.username ?? ""),
    allowedModules: credential.allowed_modules.join(", "),
    allowedServerRefs: credential.allowed_server_refs.join(", "),
    inheritToLowerScopes: credential.inherit_to_lower_scopes,
    isActive: credential.is_active,
    retainedPublicData: { ...credential.public_data }
  };
}

function credentialDraftKey(draft: CredentialDraft): string {
  return JSON.stringify(draft);
}

function splitValues(value: string): string[] {
  return [...new Set(value.split(",").map((item) => item.trim()).filter(Boolean))];
}

function secretPayload(draft: CredentialDraft): Record<string, string> {
  const secret = draft.secret.trim();
  if (!secret) return {};
  if (draft.credentialKind === "token") return { token: secret };
  if (draft.credentialKind === "api_key") return { api_key: secret };
  return { password: secret };
}

function supportedCredentialKind(value: string): CredentialKind {
  return value === "token" || value === "api_key" ? value : "username_password";
}

function secretFieldLabel(kind: CredentialKind): string {
  if (kind === "token") return "Access token";
  if (kind === "api_key") return "API key";
  return "Password";
}

function credentialKindLabel(kind: string): string {
  if (kind === "username_password") return "Username and password";
  if (kind === "api_key") return "API key";
  if (kind === "token") return "Access token";
  return kind.split("_").join(" ");
}

function availabilityLabel(credential: CredentialEnvelopeSummary): string {
  const modules = credential.allowed_modules.length
    ? credential.allowed_modules.join(", ")
    : "All modules";
  const servers = credential.allowed_server_refs.length
    ? `${credential.allowed_server_refs.length} server restriction${credential.allowed_server_refs.length === 1 ? "" : "s"}`
    : "all servers";
  return `${modules} · ${servers}${credential.inherit_to_lower_scopes ? " · inherited" : ""}`;
}

function errorMessage(error: unknown): string {
  return error instanceof Error ? error.message : String(error);
}
