import { clientFetch } from '@/lib/api/client';
import { unwrap, type ApiEnvelope, type PageParams, type Paginated } from '@/lib/api/types';
import { ADMIN_PAGE_SIZE } from '../constants';
import type {
  AdminApi,
  AdminRole,
  AuditFilters,
  AuditLogEntry,
  ConfigChange,
  Holiday,
  HolidayFilters,
  HolidayInput,
  PlatformConfig,
  RoleGrant,
  SupportAlert,
  SupportAlertFilters,
} from '../types';

const API = '/api/v1';

/** Build a `page`/`page_size` query (snake_case per b1 api-conventions). */
function pageQuery(params: PageParams, extra?: Record<string, string | undefined>): string {
  const q = new URLSearchParams();
  q.set('page', String(params.page ?? 1));
  q.set('page_size', String(params.pageSize ?? ADMIN_PAGE_SIZE));
  for (const [k, v] of Object.entries(extra ?? {})) if (v != null && v !== '') q.set(k, v);
  return q.toString();
}

/** Parse the b1 `changedFieldsJson` (`{ "Field": { "old": …, "new": … } }`) into a typed record. */
function parseChangedFields(json: string | null): AuditLogEntry['changedFields'] {
  if (!json) return null;
  try {
    return JSON.parse(json) as AuditLogEntry['changedFields'];
  } catch {
    return null;
  }
}

/** Collapse a config change's `changedFieldsJson` into the single value delta the drawer shows. */
function parseValueDelta(json: string | null): { oldValue: string | null; newValue: string | null } {
  const parsed = parseChangedFields(json);
  const field = parsed && (parsed['Value'] ?? Object.values(parsed)[0]);
  const toStr = (v: unknown): string | null => (v == null ? null : String(v));
  return { oldValue: toStr(field?.old), newValue: toStr(field?.new) };
}

interface ConfigChangeWire {
  id: number;
  action: ConfigChange['action'];
  changedFieldsJson: string | null;
  actorUserId: number | null;
  occurredAt: string;
}
interface AuditWire {
  id: number;
  entityType: string;
  entityId: string;
  action: string;
  changedFieldsJson: string | null;
  actorUserId: number | null;
  occurredAt: string;
}

/**
 * Real HTTP implementation of the `AdminApi` seam (b1 config/holiday/audit/support-alert routes + the b15
 * RBAC routes). **Not primary this phase** (`USE_ADMIN_MOCK = true`) — the config audit columns and rich
 * audit filters aren't on the wire (REQ-029/030) and the RBAC routes don't exist yet (REQ-031). When each
 * upstream lands, flip the seam in `apis/index.ts`; the hooks/screens are unchanged.
 */
export const adminClientApi: AdminApi = {
  listConfigs: async (params) =>
    unwrap(await clientFetch<ApiEnvelope<Paginated<PlatformConfig>>>(`${API}/platform_config/get_platform_configs?${pageQuery(params)}`)),

  updateConfig: async (key, value) => {
    await clientFetch<ApiEnvelope<boolean>>(`${API}/platform_config/update_platform_config`, {
      method: 'POST',
      body: JSON.stringify({ key, value }),
    });
  },

  getConfigHistory: async (key, params) => {
    const wire = unwrap(
      await clientFetch<ApiEnvelope<Paginated<ConfigChangeWire>>>(
        `${API}/platform_config/get_config_change_history?${pageQuery(params, { key })}`,
      ),
    );
    return {
      ...wire,
      items: wire.items.map((w): ConfigChange => ({
        id: w.id,
        action: w.action,
        actorUserId: w.actorUserId,
        occurredAt: w.occurredAt,
        ...parseValueDelta(w.changedFieldsJson),
      })),
    };
  },

  listHolidays: async (filters: HolidayFilters, params) =>
    unwrap(
      await clientFetch<ApiEnvelope<Paginated<Holiday>>>(
        `${API}/holidays/get_holidays?${pageQuery(params, { from: filters.from, to: filters.to })}`,
      ),
    ),

  upsertHoliday: async (input: HolidayInput) => {
    await clientFetch<ApiEnvelope<boolean>>(`${API}/holidays/upsert_holiday`, {
      method: 'POST',
      body: JSON.stringify(input),
    });
  },

  listAuditLogs: async (filters: AuditFilters, params) => {
    const wire = unwrap(
      await clientFetch<ApiEnvelope<Paginated<AuditWire>>>(
        `${API}/audit/get_audit_trail?${pageQuery(params, { entity_type: filters.entityType, entity_id: filters.entityId })}`,
      ),
    );
    return {
      ...wire,
      items: wire.items.map((w): AuditLogEntry => ({
        id: w.id,
        entityType: w.entityType,
        entityId: w.entityId,
        action: w.action,
        actorUserId: w.actorUserId,
        occurredAt: w.occurredAt,
        changedFields: parseChangedFields(w.changedFieldsJson),
      })),
    };
  },

  listSupportAlerts: async (filters: SupportAlertFilters, params) =>
    unwrap(
      await clientFetch<ApiEnvelope<Paginated<SupportAlert>>>(
        `${API}/support_alerts/get_support_alerts?${pageQuery(params, {
          type: filters.type,
          status: filters.status,
          owner_user_id: filters.ownerUserId != null ? String(filters.ownerUserId) : undefined,
        })}`,
      ),
    ),

  assignSupportAlert: async (alertId, ownerUserId) => {
    await clientFetch<ApiEnvelope<boolean>>(`${API}/support_alerts/assign_support_alert`, {
      method: 'POST',
      body: JSON.stringify({ alertId, ownerUserId }),
    });
  },

  resolveSupportAlert: async (alertId, note) => {
    await clientFetch<ApiEnvelope<boolean>>(`${API}/support_alerts/resolve_support_alert`, {
      method: 'POST',
      body: JSON.stringify({ alertId, note }),
    });
  },

  // RBAC (REQ-031 — routes proposed; not live). Kept real-shaped so the swap is one line once they ship.
  listRoles: async (userId?: number) =>
    unwrap(
      await clientFetch<ApiEnvelope<RoleGrant[]>>(
        `${API}/admin_roles/list_roles${userId != null ? `?user_id=${userId}` : ''}`,
      ),
    ),
  grantRole: async (userId, role: AdminRole) => {
    await clientFetch<ApiEnvelope<boolean>>(`${API}/admin_roles/grant_role`, {
      method: 'POST',
      body: JSON.stringify({ userId, role }),
    });
  },
  revokeRole: async (userId, role: AdminRole) => {
    await clientFetch<ApiEnvelope<boolean>>(`${API}/admin_roles/revoke_role`, {
      method: 'POST',
      body: JSON.stringify({ userId, role }),
    });
  },
};
