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

/**
 * In-memory `AdminApi` — **the primary implementation this phase** (REQ-029/030/031: config audit
 * columns, rich audit filters, and the whole RBAC surface are gaps). The fixtures are engineered to
 * exercise every console state: one config **per `dataType`** (so all typed inputs + the 0–1 rate
 * validation are reachable), a config change-history trail, holidays with **bank-closed** days, a paged
 * audit log with `changedFields` diffs, a support-alert list spanning **every** alert type/status (so the
 * worklist filters are testable), and RBAC grants. Mutations mutate the in-memory arrays so a save shows
 * on the next read. Timestamps are relative to now so Shamsi rendering always reads sensibly.
 */

const DAY_MS = 24 * 60 * 60 * 1000;
const isoDaysAgo = (d: number): string => new Date(Date.now() - d * DAY_MS).toISOString();
const dateDaysAgo = (d: number): string => isoDaysAgo(d).slice(0, 10);
const dateDaysAhead = (d: number): string => new Date(Date.now() + d * DAY_MS).toISOString().slice(0, 10);

const LATENCY_MS = 200;
const delay = <T>(v: T): Promise<T> => new Promise((r) => setTimeout(() => r(v), LATENCY_MS));

function paginate<T>(all: T[], params: PageParams): Paginated<T> {
  const page = Math.max(1, params.page ?? 1);
  const pageSize = Math.max(1, params.pageSize ?? (all.length || 1));
  const start = (page - 1) * pageSize;
  return { items: all.slice(start, start + pageSize), total: all.length, page, pageSize };
}

// ── Config (one row per data_type) ─────────────────────────────────────────────────────────────────────
const CONFIGS: PlatformConfig[] = [
  { key: 'platform_fee_rate', value: '0.15', dataType: 'decimal', description: 'Platform commission fraction of gross.', updatedAt: isoDaysAgo(30), updatedBy: 'admin@balinyaar' },
  { key: 'vat_rate', value: '0.10', dataType: 'decimal', description: 'VAT applied to the platform commission line only.', updatedAt: isoDaysAgo(90), updatedBy: 'finance@balinyaar' },
  { key: 'dispute_window_hours', value: '72', dataType: 'int', description: 'Hours after check-out before a payout becomes eligible.', updatedAt: isoDaysAgo(120), updatedBy: 'admin@balinyaar' },
  { key: 'nurse_payout_interval_days', value: '7', dataType: 'int', description: 'Payout batch cadence in days.', updatedAt: isoDaysAgo(120), updatedBy: 'admin@balinyaar' },
  { key: 'evv_location_tolerance_meters', value: '150', dataType: 'int', description: 'Advisory EVV geofence radius in meters.', updatedAt: isoDaysAgo(45), updatedBy: 'admin@balinyaar' },
  { key: 'payout_satna_threshold_irr', value: '150000000', dataType: 'int', description: 'Above this amount payouts use SATNA instead of PAYA.', updatedAt: isoDaysAgo(60), updatedBy: 'finance@balinyaar' },
  { key: 'min_rating_for_support_alert', value: '2', dataType: 'int', description: 'Reviews at or below this rating raise a low-rating alert.', updatedAt: isoDaysAgo(200), updatedBy: 'admin@balinyaar' },
  { key: 'bnpl_provider_enabled', value: 'true', dataType: 'bool', description: 'Whether the BNPL checkout branch is offered.', updatedAt: isoDaysAgo(15), updatedBy: 'admin@balinyaar' },
  { key: 'cancellation_policy_tiers', value: '{"tier1":1,"tier2":0.5,"tier3":0}', dataType: 'json', description: 'Refund fraction per cancellation lead-time tier.', updatedAt: isoDaysAgo(75), updatedBy: 'admin@balinyaar' },
  { key: 'support_contact_line', value: 'پشتیبانی بالین‌یار', dataType: 'string', description: 'Display name used in support messages.', updatedAt: isoDaysAgo(10), updatedBy: 'support@balinyaar' },
];

const CONFIG_HISTORY: Record<string, ConfigChange[]> = {
  vat_rate: [
    { id: 301, action: 'updated', actorUserId: 3, occurredAt: isoDaysAgo(90), oldValue: '0.09', newValue: '0.10' },
    { id: 300, action: 'updated', actorUserId: 3, occurredAt: isoDaysAgo(365), oldValue: '0.08', newValue: '0.09' },
  ],
  platform_fee_rate: [
    { id: 310, action: 'updated', actorUserId: 2, occurredAt: isoDaysAgo(30), oldValue: '0.18', newValue: '0.15' },
  ],
};

// ── Holidays ───────────────────────────────────────────────────────────────────────────────────────────
const HOLIDAYS: Holiday[] = [
  { id: 501, holidayDate: dateDaysAhead(3), nameFa: 'عید فطر', type: 'religious', isBankClosed: true },
  { id: 502, holidayDate: dateDaysAgo(2), nameFa: 'رحلت امام', type: 'religious', isBankClosed: true },
  { id: 503, holidayDate: dateDaysAgo(20), nameFa: 'روز جمهوری اسلامی', type: 'national', isBankClosed: true },
  { id: 504, holidayDate: dateDaysAhead(30), nameFa: 'تعطیلی اداری', type: 'official', isBankClosed: false },
];

// ── Audit log ──────────────────────────────────────────────────────────────────────────────────────────
const AUDIT: AuditLogEntry[] = [
  { id: 901, entityType: 'PlatformConfig', entityId: 'platform_fee_rate', action: 'updated', actorUserId: 2, occurredAt: isoDaysAgo(30), changedFields: { Value: { old: '0.18', new: '0.15' } } },
  { id: 902, entityType: 'Refund', entityId: '7', action: 'created', actorUserId: 4, occurredAt: isoDaysAgo(4), changedFields: { Status: { old: null, new: 'succeeded' }, Amount: { old: null, new: '10000000' } } },
  { id: 903, entityType: 'NurseVerification', entityId: '15', action: 'updated', actorUserId: 2, occurredAt: isoDaysAgo(6), changedFields: { Status: { old: 'in_review', new: 'approved' }, IsVerified: { old: false, new: true } } },
  { id: 904, entityType: 'PayoutBatch', entityId: '7003', action: 'created', actorUserId: 5, occurredAt: isoDaysAgo(1), changedFields: { Status: { old: null, new: 'processing' }, PayoutCount: { old: null, new: 12 } } },
  { id: 905, entityType: 'Review', entityId: '44', action: 'updated', actorUserId: 6, occurredAt: isoDaysAgo(2), changedFields: { ModerationStatus: { old: 'pending_moderation', new: 'published' } } },
  { id: 906, entityType: 'PartnerCenter', entityId: '1', action: 'updated', actorUserId: 2, occurredAt: isoDaysAgo(8), changedFields: { SettlementIban: { old: '<redacted>', new: '<redacted>' }, IsActive: { old: false, new: true } } },
];

// ── Support alerts (one of every type; all statuses) ────────────────────────────────────────────────────
const ALERTS: SupportAlert[] = [
  { id: 801, type: 'low_rating', severity: 'medium', status: 'open', entityType: 'Review', entityId: '44', bookingId: 5001, reviewId: 44, ownerUserId: null, resolutionNote: null, resolvedAt: null, createdAt: isoDaysAgo(1) },
  { id: 802, type: 'evv_no_show', severity: 'high', status: 'open', entityType: 'Booking', entityId: '5002', bookingId: 5002, reviewId: null, ownerUserId: null, resolutionNote: null, resolvedAt: null, createdAt: isoDaysAgo(2) },
  { id: 803, type: 'evv_location_mismatch', severity: 'medium', status: 'assigned', entityType: 'Booking', entityId: '5003', bookingId: 5003, reviewId: null, ownerUserId: 3, resolutionNote: null, resolvedAt: null, createdAt: isoDaysAgo(3) },
  { id: 804, type: 'verification_expired', severity: 'high', status: 'open', entityType: 'NurseVerification', entityId: '15', bookingId: null, reviewId: null, ownerUserId: null, resolutionNote: null, resolvedAt: null, createdAt: isoDaysAgo(4) },
  { id: 805, type: 'shared_sim', severity: 'high', status: 'assigned', entityType: 'NurseVerification', entityId: '16', bookingId: null, reviewId: null, ownerUserId: 3, resolutionNote: null, resolvedAt: null, createdAt: isoDaysAgo(5) },
  { id: 806, type: 'payment_anomaly', severity: 'high', status: 'open', entityType: 'PaymentTransaction', entityId: '3001', bookingId: 5004, reviewId: null, ownerUserId: null, resolutionNote: null, resolvedAt: null, createdAt: isoDaysAgo(6) },
  { id: 807, type: 'fraud_signal', severity: 'high', status: 'open', entityType: 'User', entityId: '7099', bookingId: null, reviewId: null, ownerUserId: null, resolutionNote: null, resolvedAt: null, createdAt: isoDaysAgo(7) },
  { id: 808, type: 'nurse_clawback', severity: 'medium', status: 'resolved', entityType: 'NurseClawback', entityId: '210', bookingId: 5004, reviewId: null, ownerUserId: 4, resolutionNote: 'Netted in the next payout batch.', resolvedAt: isoDaysAgo(1), createdAt: isoDaysAgo(9) },
  { id: 809, type: 'emergency', severity: 'high', status: 'resolved', entityType: 'Booking', entityId: '5001', bookingId: 5001, reviewId: null, ownerUserId: 3, resolutionNote: 'Called 115; patient stable.', resolvedAt: isoDaysAgo(2), createdAt: isoDaysAgo(10) },
];

// ── RBAC grants ──────────────────────────────────────────────────────────────────────────────────────
const ROLES: RoleGrant[] = [
  { userId: 2, role: 'admin', grantedBy: 1, grantedAt: isoDaysAgo(400), revokedAt: null },
  { userId: 3, role: 'support', grantedBy: 2, grantedAt: isoDaysAgo(200), revokedAt: null },
  { userId: 4, role: 'finance', grantedBy: 2, grantedAt: isoDaysAgo(180), revokedAt: null },
  { userId: 5, role: 'finance', grantedBy: 2, grantedAt: isoDaysAgo(90), revokedAt: null },
  { userId: 6, role: 'moderation', grantedBy: 2, grantedAt: isoDaysAgo(60), revokedAt: null },
];

export const adminMockApi: AdminApi = {
  listConfigs: async (params) => delay(paginate([...CONFIGS], params)),

  updateConfig: async (key, value) => {
    const row = CONFIGS.find((c) => c.key === key);
    if (!row) throw new Error(`Mock config ${key} not found`);
    const old = row.value;
    row.value = value;
    row.updatedAt = new Date().toISOString();
    row.updatedBy = 'you@balinyaar';
    (CONFIG_HISTORY[key] ??= []).unshift({
      id: Math.floor(1000 + (CONFIG_HISTORY[key]?.length ?? 0)),
      action: 'updated',
      actorUserId: 1,
      occurredAt: row.updatedAt,
      oldValue: old,
      newValue: value,
    });
    return delay(undefined);
  },

  getConfigHistory: async (key, params) => delay(paginate([...(CONFIG_HISTORY[key] ?? [])], params)),

  listHolidays: async (filters, params) => {
    let items = [...HOLIDAYS];
    if (filters.from) items = items.filter((h) => h.holidayDate >= filters.from!);
    if (filters.to) items = items.filter((h) => h.holidayDate <= filters.to!);
    items.sort((a, b) => a.holidayDate.localeCompare(b.holidayDate));
    return delay(paginate(items, params));
  },

  upsertHoliday: async (input) => {
    const existing = HOLIDAYS.find((h) => h.holidayDate === input.holidayDate);
    if (existing) Object.assign(existing, input);
    else HOLIDAYS.push({ id: Math.max(0, ...HOLIDAYS.map((h) => h.id)) + 1, ...input });
    return delay(undefined);
  },

  listAuditLogs: async (filters, params) => {
    let items = [...AUDIT];
    if (filters.entityType) items = items.filter((a) => a.entityType.toLowerCase().includes(filters.entityType!.toLowerCase()));
    if (filters.entityId) items = items.filter((a) => a.entityId === filters.entityId);
    if (filters.actorUserId != null) items = items.filter((a) => a.actorUserId === filters.actorUserId);
    if (filters.action) items = items.filter((a) => a.action === filters.action);
    if (filters.from) items = items.filter((a) => a.occurredAt >= filters.from!);
    if (filters.to) items = items.filter((a) => a.occurredAt <= filters.to!);
    items.sort((a, b) => b.occurredAt.localeCompare(a.occurredAt));
    return delay(paginate(items, params));
  },

  listSupportAlerts: async (filters, params) => {
    let items = [...ALERTS];
    if (filters.type) items = items.filter((a) => a.type === filters.type);
    if (filters.status) items = items.filter((a) => a.status === filters.status);
    if (filters.ownerUserId != null) items = items.filter((a) => a.ownerUserId === filters.ownerUserId);
    items.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
    return delay(paginate(items, params));
  },

  assignSupportAlert: async (alertId, ownerUserId) => {
    const a = ALERTS.find((x) => x.id === alertId);
    if (!a || a.status === 'resolved') throw new Error(`Mock alert ${alertId} not assignable`);
    a.status = 'assigned';
    a.ownerUserId = ownerUserId;
    return delay(undefined);
  },

  resolveSupportAlert: async (alertId, note) => {
    const a = ALERTS.find((x) => x.id === alertId);
    if (!a || a.status === 'resolved') throw new Error(`Mock alert ${alertId} not resolvable`);
    a.status = 'resolved';
    a.resolutionNote = note;
    a.resolvedAt = new Date().toISOString();
    return delay(undefined);
  },

  listRoles: async (userId) => delay(userId == null ? [...ROLES] : ROLES.filter((r) => r.userId === userId)),

  grantRole: async (userId, role) => {
    const existing = ROLES.find((r) => r.userId === userId && r.role === role);
    if (existing) existing.revokedAt = null;
    else ROLES.push({ userId, role, grantedBy: 1, grantedAt: new Date().toISOString(), revokedAt: null });
    return delay(undefined);
  },

  revokeRole: async (userId, role) => {
    const r = ROLES.find((x) => x.userId === userId && x.role === role && !x.revokedAt);
    if (r) r.revokedAt = new Date().toISOString();
    return delay(undefined);
  },
};
