import { useEffect, useMemo, useState, type ReactNode } from "react";
import type { ApiSettings } from "../../types";
import {
  getPrivacyRetentionPolicy,
  updatePrivacyRetentionPolicy,
  type PrivacyRetentionLimitPermissionPatch,
  type PrivacyRetentionLimitPermissions,
  type PrivacyRetentionPolicy,
  type PrivacyRetentionPolicyFieldKey,
  type PrivacyRetentionPolicyPatch,
  type PrivacyRetentionPolicyScope
} from "../../api/admin";
import Button from "../../components/Button";
import Card from "../../components/Card";
import DismissibleAlert from "../../components/DismissibleAlert";
import type { PolicySourcePathItem } from "../../components/PolicySourcePath";
import FieldLabel from "../../components/help/FieldLabel";
import FormField from "../../components/FormField";
import ToggleSwitch from "../../components/ToggleSwitch";

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

type RetentionPolicyScopeManagerProps = {
  settings: ApiSettings;
  scopeType: PrivacyRetentionPolicyScope;
  scopeId?: string | null;
  targetOptions?: RetentionPolicyTargetOption[];
  targetLabel?: string;
  title?: string;
  description?: string;
  canWrite: boolean;
  locked?: boolean;
};

type RetentionPolicyEditorProps = {
  settings: ApiSettings;
  scopeType: PrivacyRetentionPolicyScope;
  scopeId?: string | null;
  title?: string;
  description?: string;
  canWrite: boolean;
  locked?: boolean;
};

type DayKey =
  | "raw_campaign_json_retention_days"
  | "generated_eml_retention_days"
  | "stored_report_detail_retention_days"
  | "mock_mailbox_retention_days"
  | "audit_detail_retention_days";

type FieldDefinition = {
  key: PrivacyRetentionPolicyFieldKey;
  label: string;
  kind: "raw-json" | "days" | "audit";
  systemOnly?: boolean;
};

type RawJsonValue = "inherit" | "keep" | "disable";
type AuditDetailValue = "inherit" | PrivacyRetentionPolicy["audit_detail_level"];

const auditDetailOrder: Record<PrivacyRetentionPolicy["audit_detail_level"], number> = { full: 0, redacted: 1, minimal: 2 };

const defaultAllowLowerLevelLimits: PrivacyRetentionLimitPermissions = {
  store_raw_campaign_json: true,
  raw_campaign_json_retention_days: true,
  generated_eml_retention_days: true,
  stored_report_detail_retention_days: true,
  mock_mailbox_retention_days: true,
  audit_detail_retention_days: true,
  audit_detail_level: true
};

const defaultPrivacyPolicy: PrivacyRetentionPolicy = {
  store_raw_campaign_json: true,
  raw_campaign_json_retention_days: null,
  generated_eml_retention_days: null,
  stored_report_detail_retention_days: null,
  mock_mailbox_retention_days: null,
  audit_detail_retention_days: null,
  audit_detail_level: "full",
  allow_lower_level_limits: defaultAllowLowerLevelLimits
};

const fieldDefinitions: FieldDefinition[] = [
  { key: "store_raw_campaign_json", label: "Raw campaign JSON", kind: "raw-json" },
  { key: "raw_campaign_json_retention_days", label: "Raw campaign JSON days", kind: "days" },
  { key: "generated_eml_retention_days", label: "Generated EML days", kind: "days" },
  { key: "stored_report_detail_retention_days", label: "Stored report detail days", kind: "days" },
  { key: "mock_mailbox_retention_days", label: "Mock mailbox days", kind: "days", systemOnly: true },
  { key: "audit_detail_retention_days", label: "Audit detail days", kind: "days" },
  { key: "audit_detail_level", label: "Audit detail level", kind: "audit" }
];

export function RetentionPolicyScopeManager({
  settings,
  scopeType,
  scopeId = null,
  targetOptions = [],
  targetLabel = "Target",
  title = "Retention policy",
  description,
  canWrite,
  locked = false
}: RetentionPolicyScopeManagerProps) {
  const [selectedTargetId, setSelectedTargetId] = useState(scopeId || targetOptions[0]?.id || "");
  const requiresTarget = scopeType === "user" || scopeType === "group" || scopeType === "campaign";
  const targetSelectionRequired = requiresTarget && !scopeId;
  const hasSelectableTarget = targetOptions.length > 0;
  const activeScopeId = scopeId || selectedTargetId || null;
  const defaultDescription = scopeType === "system"
    ? "System retention defaults and the fields lower levels may override."
    : "Local retention limits for this scope. Values inherit from the parent unless explicitly narrowed.";
  const targetEmptyText = `No ${pluralizeLabel(targetLabel.toLowerCase())} available`;

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

  return (
    <div className="retention-policy-manager">
      {targetSelectionRequired && (
        <Card title={`${targetLabel} scope`}>
          <div className="retention-policy-target-row">
            <FormField label={targetLabel}>
              <select value={selectedTargetId} disabled={!hasSelectableTarget} onChange={(event) => setSelectedTargetId(event.target.value)}>
                {!hasSelectableTarget && <option value="">{targetEmptyText}</option>}
                {targetOptions.map((option) => <option key={option.id} value={option.id}>{option.secondary ? `${option.label} (${option.secondary})` : option.label}</option>)}
              </select>
            </FormField>
          </div>
        </Card>
      )}
      {(!requiresTarget || Boolean(activeScopeId)) && (
        <RetentionPolicyEditor
          settings={settings}
          scopeType={scopeType}
          scopeId={activeScopeId}
          title={title}
          description={description ?? defaultDescription}
          canWrite={canWrite}
          locked={locked}
        />
      )}
    </div>
  );
}

function pluralizeLabel(label: string): string {
  if (label.endsWith("s")) return label;
  if (label.endsWith("y")) return `${label.slice(0, -1)}ies`;
  return `${label}s`;
}

export function RetentionPolicyEditor({
  settings,
  scopeType,
  scopeId = null,
  title = "Retention policy",
  description,
  canWrite,
  locked = false
}: RetentionPolicyEditorProps) {
  const [policy, setPolicy] = useState<PrivacyRetentionPolicyPatch>({});
  const [effectivePolicy, setEffectivePolicy] = useState<PrivacyRetentionPolicy | null>(null);
  const [parentPolicy, setParentPolicy] = useState<PrivacyRetentionPolicy | null>(null);
  const [effectivePolicySources, setEffectivePolicySources] = useState<PolicySourcePathItem[]>([]);
  const [loading, setLoading] = useState(false);
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState("");
  const [success, setSuccess] = useState("");

  const isSystem = scopeType === "system";
  const requiresTarget = scopeType === "user" || scopeType === "group" || scopeType === "campaign";
  const scopeReady = !requiresTarget || Boolean(scopeId);
  const disabled = locked || loading || busy || !canWrite || !scopeReady;
  const showAllowColumn = scopeType !== "campaign";
  const showEffectiveColumn = !isSystem && scopeReady;
  const activeParentPolicy = parentPolicy ? normalizeFullPolicy(parentPolicy) : null;
  const activeFullPolicy = normalizeFullPolicy({ ...defaultPrivacyPolicy, ...policy, allow_lower_level_limits: { ...defaultAllowLowerLevelLimits, ...(policy.allow_lower_level_limits ?? {}) } });
  const visibleFields = useMemo(() => fieldDefinitions.filter((field) => isSystem || !field.systemOnly), [isSystem]);

  useEffect(() => { void loadPolicy(); }, [settings.accessToken, settings.apiBaseUrl, settings.apiKey, scopeType, scopeId]);

  async function loadPolicy() {
    setError("");
    setSuccess("");
    if (!scopeReady) {
      setPolicy({});
      setEffectivePolicy(null);
      setParentPolicy(null);
      setEffectivePolicySources([]);
      return;
    }
    setLoading(true);
    try {
      const response = await getPrivacyRetentionPolicy(settings, scopeType, scopeId);
      setPolicy(isSystem ? normalizeFullPolicy(response.policy) : normalizePatch(response.policy));
      setEffectivePolicy(normalizeFullPolicy(response.effective_policy));
      setParentPolicy(response.parent_policy ? normalizeFullPolicy(response.parent_policy) : null);
      setEffectivePolicySources(response.effective_policy_sources ?? []);
    } catch (err) {
      setPolicy({});
      setEffectivePolicy(null);
      setParentPolicy(null);
      setEffectivePolicySources([]);
      setError(errorMessage(err));
    } finally {
      setLoading(false);
    }
  }

  async function savePolicy() {
    if (!scopeReady) return;
    setBusy(true);
    setError("");
    setSuccess("");
    try {
      const payload = isSystem ? normalizeFullPolicy(policy) : normalizePatchForSave(policy, activeParentPolicy, scopeType);
      const response = await updatePrivacyRetentionPolicy(settings, scopeType, payload, scopeId);
      setPolicy(isSystem ? normalizeFullPolicy(response.policy) : normalizePatch(response.policy));
      setEffectivePolicy(normalizeFullPolicy(response.effective_policy));
      setParentPolicy(response.parent_policy ? normalizeFullPolicy(response.parent_policy) : null);
      setEffectivePolicySources(response.effective_policy_sources ?? []);
      setSuccess("Retention policy saved.");
    } catch (err) {
      setError(errorMessage(err));
    } finally {
      setBusy(false);
    }
  }

  function patchPolicy(patch: PrivacyRetentionPolicyPatch) {
    const next = { ...policy, ...patch };
    if (policy.allow_lower_level_limits || patch.allow_lower_level_limits) {
      next.allow_lower_level_limits = { ...(policy.allow_lower_level_limits ?? {}), ...(patch.allow_lower_level_limits ?? {}) };
    }
    setPolicy(isSystem ? normalizeFullPolicy(next) : normalizePatch(next));
  }

  function setRawJson(value: RawJsonValue | "store" | "disabled") {
    if (isSystem) {
      patchPolicy({ store_raw_campaign_json: value === "store" });
      return;
    }
    patchPolicy({ store_raw_campaign_json: value === "inherit" ? undefined : value === "keep" });
  }

  function setRetentionDays(key: DayKey, value: string) {
    const trimmed = value.trim();
    patchPolicy({ [key]: trimmed === "" ? (isSystem ? null : undefined) : Math.max(0, Number(trimmed) || 0) });
  }

  function setAuditDetail(value: AuditDetailValue) {
    patchPolicy({ audit_detail_level: value === "inherit" ? undefined : value });
  }

  function setAllowLowerLevelLimit(key: PrivacyRetentionPolicyFieldKey, allowed: boolean) {
    patchPolicy({ allow_lower_level_limits: { [key]: allowed } });
  }

  function parentAllows(key: PrivacyRetentionPolicyFieldKey): boolean {
    return !activeParentPolicy || activeParentPolicy.allow_lower_level_limits[key] !== false;
  }

  function localAllowsLower(key: PrivacyRetentionPolicyFieldKey): boolean {
    if (isSystem) return activeFullPolicy.allow_lower_level_limits[key] !== false;
    const localValue = policy.allow_lower_level_limits?.[key];
    if (localValue !== undefined) return localValue && parentAllows(key);
    return parentAllows(key);
  }

  const localPolicySummary = useMemo(() => isSystem ? "System baseline" : localPolicyCount(policy), [isSystem, policy]);
  const defaultDescription = isSystem
    ? "Set concrete system retention values. Blank day fields mean unlimited retention."
    : "Blank values inherit. Numeric values may only shorten the inherited retention period; audit detail may only become more restrictive.";

  return (
    <Card
      title={title}
      actions={
        <div className="button-row compact-actions">
          <Button onClick={() => void loadPolicy()} disabled={loading || !scopeReady}>{loading ? "Loading…" : "Reload"}</Button>
          <Button variant="primary" onClick={() => void savePolicy()} disabled={disabled}>{busy ? "Saving…" : "Save policy"}</Button>
        </div>
      }
    >
      <div className="retention-policy-editor">
        <p className="muted small-note retention-policy-description">{description ?? defaultDescription}</p>
        {!isSystem && <p className="muted small-note retention-policy-description">Mock mailbox retention is currently managed at system level because mock mailbox records do not carry tenant or campaign ownership metadata.</p>}
        {error && <DismissibleAlert tone="danger" resetKey={error} floating>{error}</DismissibleAlert>}
        {success && <DismissibleAlert tone="success" resetKey={success} floating>{success}</DismissibleAlert>}

        <section className="retention-policy-section">
          <div className="subsection-heading split">
            <h3>{isSystem ? "System policy" : "Local policy"}</h3>
            <span className="muted small-note">{localPolicySummary}</span>
          </div>
          <div className={`retention-policy-table policy-table${showEffectiveColumn ? " with-effective-column" : ""}${showAllowColumn ? " with-allow-column" : ""}`}>
            <div className="retention-policy-row policy-row retention-policy-row-header policy-row-header">
              <span>Field</span>
              <span>{isSystem ? "Value" : "Local setting"}</span>
              {showEffectiveColumn && <span>Effective policy</span>}
              {showAllowColumn && <span>Lower levels</span>}
            </div>
            {visibleFields.map((field) => {
              const fieldLocked = !parentAllows(field.key);
              const fieldDisabled = disabled || fieldLocked;
              return (
                <div className="retention-policy-row policy-row" key={field.key}>
                  <div className="retention-policy-field-label policy-field-label">
                    <strong>{field.label}</strong>
                    {fieldLocked && <small>Locked by parent policy</small>}
                    {field.systemOnly && <small>System-level cleanup scope</small>}
                  </div>
                  <div className="policy-control">{renderFieldControl(field, activeFullPolicy, policy, isSystem, fieldDisabled, setRawJson, setRetentionDays, setAuditDetail, activeParentPolicy)}</div>
                  {showEffectiveColumn && (
                    <div className="retention-policy-effective-cell policy-effective-cell">
                      <FieldLabel className="retention-policy-effective-value policy-effective-value" help={retentionPolicyPathHelp(field, effectivePolicySources, scopeType)}>
                        {retentionFieldValue(field, effectivePolicy, loading)}
                      </FieldLabel>
                    </div>
                  )}
                  {showAllowColumn && (
                    <ToggleSwitch
                      checked={localAllowsLower(field.key)}
                      disabled={disabled || fieldLocked}
                      onChange={(checked) => setAllowLowerLevelLimit(field.key, checked)}
                      label="Allow override"
                    />
                  )}
                </div>
              );
            })}
          </div>
        </section>

      </div>
    </Card>
  );
}

type PolicySourceItem = {
  label: string;
  policy?: Record<string, unknown> | null;
};

function retentionPolicyPathHelp(field: FieldDefinition, sources: PolicySourcePathItem[], scopeType: PrivacyRetentionPolicyScope): ReactNode {
  const items = normalizePolicySourceItems(sources.length > 0 ? sources : retentionPolicySourcePath(scopeType));
  return <PolicyPathHelp lines={retentionPolicyPathLines(field, items)} />;
}

function PolicyPathHelp({ lines }: { lines: string[] }) {
  return (
    <span className="policy-path-help">
      {lines.map((line, index) => <span className="policy-path-help-line" key={`${line}-${index}`}>{line}</span>)}
    </span>
  );
}

function retentionPolicyPathLines(field: FieldDefinition, items: PolicySourceItem[]): string[] {
  if (items.length === 0) return ["System: Default"];
  const lines: string[] = [];
  for (const [index, item] of items.entries()) {
    const sourcePolicy = asRecord(item.policy);
    const value = retentionSourceValue(field, sourcePolicy, index === 0);
    const locked = asRecord(sourcePolicy.allow_lower_level_limits)[field.key] === false;
    lines.push(`${policyPathPrefix(index)}${item.label}: ${locked ? `${value} without override` : value}`);
    if (locked) break;
  }
  return lines;
}

function retentionSourceValue(field: FieldDefinition, sourcePolicy: Record<string, unknown>, isSystem: boolean): string {
  if (Object.keys(sourcePolicy).length === 0) return isSystem ? "Default" : "Inherit";
  if (!isSystem && !(field.key in sourcePolicy)) return "Inherit";
  if (field.kind === "raw-json") {
    const value = sourcePolicy.store_raw_campaign_json;
    return value === false ? "Disabled" : value === true ? "Stored" : "Inherit";
  }
  if (field.kind === "audit") {
    const value = sourcePolicy.audit_detail_level;
    return value === "full" || value === "redacted" || value === "minimal" ? auditDetailLabel(value) : "Inherit";
  }
  const value = sourcePolicy[field.key];
  return typeof value === "number" ? daysLabel(value) : value === null && isSystem ? daysLabel(null) : "Inherit";
}

function normalizePolicySourceItems(items: PolicySourcePathItem[]): PolicySourceItem[] {
  return items.map((item) => {
    if (typeof item === "string") return { label: item };
    return { label: item.label, policy: item.policy };
  }).filter((item) => item.label.trim());
}

function asRecord(value: unknown): Record<string, unknown> {
  return value && typeof value === "object" && !Array.isArray(value) ? value as Record<string, unknown> : {};
}

function policyPathPrefix(index: number): string {
  return index === 0 ? "" : `${"  ".repeat(index - 1)}> `;
}

function retentionFieldValue(field: FieldDefinition, policy: PrivacyRetentionPolicy | null, loading: boolean): string {
  if (!policy) return loading ? "Loading..." : "Unavailable";
  if (field.kind === "raw-json") return policy.store_raw_campaign_json ? "Stored" : "Disabled";
  if (field.kind === "audit") return auditDetailLabel(policy.audit_detail_level);
  return daysLabel(policy[field.key as DayKey]);
}

function renderFieldControl(
  field: FieldDefinition,
  fullPolicy: PrivacyRetentionPolicy,
  patchPolicy: PrivacyRetentionPolicyPatch,
  isSystem: boolean,
  disabled: boolean,
  setRawJson: (value: RawJsonValue | "store" | "disabled") => void,
  setRetentionDays: (key: DayKey, value: string) => void,
  setAuditDetail: (value: AuditDetailValue) => void,
  parentPolicy: PrivacyRetentionPolicy | null
) {
  if (field.kind === "raw-json") {
    if (isSystem) {
      return <RawJsonSystemSelect value={fullPolicy.store_raw_campaign_json ? "store" : "disabled"} disabled={disabled} onChange={setRawJson} />;
    }
    return <RawJsonScopedSelect value={rawJsonValue(patchPolicy.store_raw_campaign_json)} parentStoresRawJson={parentPolicy?.store_raw_campaign_json ?? true} disabled={disabled} onChange={setRawJson} />;
  }
  if (field.kind === "audit") {
    return <AuditDetailSelect value={isSystem ? fullPolicy.audit_detail_level : auditDetailValue(patchPolicy.audit_detail_level)} includeInherit={!isSystem} parentValue={isSystem ? null : parentPolicy?.audit_detail_level ?? null} disabled={disabled} onChange={setAuditDetail} />;
  }
  const parentDayLimit = !isSystem && parentPolicy ? parentPolicy[field.key as DayKey] : null;
  return (
    <RetentionDaysField
      value={isSystem ? fullPolicy[field.key as DayKey] : patchPolicy[field.key as DayKey]}
      disabled={disabled}
      placeholder={isSystem ? "Unlimited" : "Inherit"}
      max={parentDayLimit}
      onChange={(value) => setRetentionDays(field.key as DayKey, value)}
    />
  );
}

function RawJsonSystemSelect({ value, disabled, onChange }: { value: "store" | "disabled"; disabled: boolean; onChange: (value: "store" | "disabled") => void }) {
  return (
    <select value={value} disabled={disabled} onChange={(event) => onChange(event.target.value as "store" | "disabled")}>
      <option value="store">Store</option>
      <option value="disabled">Disable storage</option>
    </select>
  );
}

function RawJsonScopedSelect({ value, parentStoresRawJson, disabled, onChange }: { value: RawJsonValue; parentStoresRawJson: boolean; disabled: boolean; onChange: (value: RawJsonValue) => void }) {
  return (
    <select value={value} disabled={disabled} onChange={(event) => onChange(event.target.value as RawJsonValue)}>
      <option value="inherit">Inherit</option>
      <option value="keep" disabled={!parentStoresRawJson}>Store if parent allows</option>
      <option value="disable">Disable storage</option>
    </select>
  );
}

function RetentionDaysField({ value, disabled, placeholder, max, onChange }: { value?: number | null; disabled: boolean; placeholder: string; max?: number | null; onChange: (value: string) => void }) {
  function handleChange(nextValue: string) {
    const trimmed = nextValue.trim();
    if (trimmed === "" || max === null || max === undefined) {
      onChange(nextValue);
      return;
    }
    const parsed = Number(trimmed);
    onChange(Number.isFinite(parsed) && parsed > max ? String(max) : nextValue);
  }

  return <input type="number" min={0} max={max ?? undefined} value={value ?? ""} disabled={disabled} placeholder={placeholder} onChange={(event) => handleChange(event.target.value)} />;
}

function AuditDetailSelect({ value, includeInherit, parentValue, disabled, onChange }: { value: AuditDetailValue; includeInherit: boolean; parentValue?: PrivacyRetentionPolicy["audit_detail_level"] | null; disabled: boolean; onChange: (value: AuditDetailValue) => void }) {
  const minimumOrder = parentValue ? auditDetailOrder[parentValue] : 0;
  function optionDisabled(option: PrivacyRetentionPolicy["audit_detail_level"]): boolean {
    return auditDetailOrder[option] < minimumOrder;
  }

  return (
    <select value={value} disabled={disabled} onChange={(event) => onChange(event.target.value as AuditDetailValue)}>
      {includeInherit && <option value="inherit">Inherit</option>}
      <option value="full" disabled={optionDisabled("full")}>Full</option>
      <option value="redacted" disabled={optionDisabled("redacted")}>Redacted</option>
      <option value="minimal" disabled={optionDisabled("minimal")}>Minimal</option>
    </select>
  );
}

function rawJsonValue(value: boolean | undefined): RawJsonValue {
  if (value === undefined) return "inherit";
  return value ? "keep" : "disable";
}

function auditDetailValue(value: PrivacyRetentionPolicyPatch["audit_detail_level"]): AuditDetailValue {
  return value ?? "inherit";
}

function daysLabel(value?: number | null): string {
  return value === null || value === undefined ? "Unlimited" : `${value} day${value === 1 ? "" : "s"}`;
}

function auditDetailLabel(value: PrivacyRetentionPolicy["audit_detail_level"]): string {
  if (value === "full") return "Full";
  if (value === "redacted") return "Redacted";
  return "Minimal";
}

function localPolicyCount(policy: PrivacyRetentionPolicyPatch): string {
  let count = 0;
  for (const field of fieldDefinitions) {
    if (field.systemOnly) continue;
    if (policy[field.key as keyof PrivacyRetentionPolicyPatch] !== undefined && policy[field.key as keyof PrivacyRetentionPolicyPatch] !== null) count += 1;
  }
  const allowCount = Object.keys(policy.allow_lower_level_limits ?? {}).length;
  count += allowCount;
  return count === 0 ? "Fully inherited" : `${count} local override${count === 1 ? "" : "s"}`;
}

function retentionPolicySourcePath(scopeType: PrivacyRetentionPolicyScope): string[] {
  if (scopeType === "system") return ["System"];
  if (scopeType === "tenant") return ["System", "Tenant"];
  if (scopeType === "user") return ["System", "Tenant", "User"];
  if (scopeType === "group") return ["System", "Tenant", "Group"];
  return ["System", "Tenant", "Owner policy", "Campaign"];
}

function normalizeAllowLimits(value: PrivacyRetentionLimitPermissionPatch | PrivacyRetentionLimitPermissions | null | undefined): PrivacyRetentionLimitPermissions {
  return { ...defaultAllowLowerLevelLimits, ...(value ?? {}) };
}

function normalizeFullPolicy(policy: PrivacyRetentionPolicyPatch | Partial<PrivacyRetentionPolicy> | null | undefined): PrivacyRetentionPolicy {
  return {
    ...defaultPrivacyPolicy,
    ...(policy ?? {}),
    raw_campaign_json_retention_days: policy?.raw_campaign_json_retention_days ?? null,
    generated_eml_retention_days: policy?.generated_eml_retention_days ?? null,
    stored_report_detail_retention_days: policy?.stored_report_detail_retention_days ?? null,
    mock_mailbox_retention_days: policy?.mock_mailbox_retention_days ?? null,
    audit_detail_retention_days: policy?.audit_detail_retention_days ?? null,
    allow_lower_level_limits: normalizeAllowLimits(policy?.allow_lower_level_limits)
  };
}

function normalizePatch(policy: PrivacyRetentionPolicyPatch | null | undefined): PrivacyRetentionPolicyPatch {
  const normalized: PrivacyRetentionPolicyPatch = {};
  if (!policy) return normalized;
  if (policy.store_raw_campaign_json !== undefined) normalized.store_raw_campaign_json = policy.store_raw_campaign_json;
  for (const field of fieldDefinitions) {
    if (field.kind !== "days" || field.systemOnly) continue;
    const value = policy[field.key as DayKey];
    if (value !== undefined && value !== null) normalized[field.key as DayKey] = Math.max(0, Number(value) || 0);
  }
  if (policy.audit_detail_level) normalized.audit_detail_level = policy.audit_detail_level;
  if (policy.allow_lower_level_limits) normalized.allow_lower_level_limits = { ...policy.allow_lower_level_limits };
  return normalized;
}

function normalizePatchForSave(policy: PrivacyRetentionPolicyPatch, parentPolicy: PrivacyRetentionPolicy | null, scopeType: PrivacyRetentionPolicyScope): PrivacyRetentionPolicyPatch {
  const normalized = normalizePatch(policy);
  const allowPatch = { ...(normalized.allow_lower_level_limits ?? {}) };
  for (const field of fieldDefinitions) {
    if (field.systemOnly || scopeType === "campaign") delete allowPatch[field.key];
    if (field.systemOnly) delete normalized[field.key as keyof PrivacyRetentionPolicyPatch];
    if (parentPolicy && parentPolicy.allow_lower_level_limits[field.key] === false) {
      delete normalized[field.key as keyof PrivacyRetentionPolicyPatch];
      delete allowPatch[field.key];
    }
  }
  if (Object.keys(allowPatch).length > 0) normalized.allow_lower_level_limits = allowPatch;
  else delete normalized.allow_lower_level_limits;
  return normalized;
}

function errorMessage(err: unknown): string {
  if (err instanceof Error) return err.message;
  return "Request failed";
}
