// Admin-only helpers. Every call is RLS-gated server-side via the
// `current_user_is_admin()` helper + admin-specific policies. Non-admins
// trying to call these still get clean PostgREST 403/empty-result responses.

import type { AppSupabaseClient } from '../supabase/client.js';

// --- Admin settings -------------------------------------------------------

export interface AdminSetting {
  key: string;
  value: unknown;
  updatedAt: string;
}

export async function listAdminSettings(client: AppSupabaseClient): Promise<AdminSetting[]> {
  const { data, error } = await client
    .from('admin_settings')
    .select('key, value, updated_at')
    .order('key', { ascending: true });
  if (error) throw error;
  return (data ?? []).map((r) => ({ key: r.key, value: r.value, updatedAt: r.updated_at }));
}

export async function updateAdminSetting(
  client: AppSupabaseClient,
  key: string,
  value: unknown,
): Promise<void> {
  const { error } = await client
    .from('admin_settings')
    .upsert(
      {
        key,
        value: value as never,
        updated_at: new Date().toISOString(),
      } as never,
      { onConflict: 'key' },
    );
  if (error) throw error;
}

// --- Invite codes ---------------------------------------------------------

export interface InviteRecord {
  code: string;
  createdBy: string | null;
  usesLimit: number | null;
  usesCount: number;
  expiresAt: string | null;
  disabled: boolean;
  createdAt: string;
}

function mapInvite(row: {
  code: string;
  created_by: string | null;
  uses_limit: number | null;
  uses_count: number;
  expires_at: string | null;
  disabled: boolean;
  created_at: string;
}): InviteRecord {
  return {
    code: row.code,
    createdBy: row.created_by,
    usesLimit: row.uses_limit,
    usesCount: row.uses_count,
    expiresAt: row.expires_at,
    disabled: row.disabled,
    createdAt: row.created_at,
  };
}

export async function listInvites(client: AppSupabaseClient): Promise<InviteRecord[]> {
  const { data, error } = await client
    .from('invites')
    .select('code, created_by, uses_limit, uses_count, expires_at, disabled, created_at')
    .order('created_at', { ascending: false });
  if (error) throw error;
  return (data ?? []).map(mapInvite);
}

export interface CreateInviteParams {
  code?: string; // omit to auto-generate
  usesLimit?: number | null;
  expiresAt?: string | null; // ISO
}

const INVITE_ALPHABET = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
function randomCode(length = 10): string {
  const buf =
    typeof crypto !== 'undefined' && 'getRandomValues' in crypto
      ? crypto.getRandomValues(new Uint8Array(length))
      : null;
  let out = '';
  for (let i = 0; i < length; i++) {
    const v = buf ? buf[i]! : Math.floor(Math.random() * 256);
    const ch = INVITE_ALPHABET[v % INVITE_ALPHABET.length];
    if (ch !== undefined) out += ch;
  }
  return out;
}

export async function createInvite(
  client: AppSupabaseClient,
  params: CreateInviteParams = {},
): Promise<InviteRecord> {
  const { data: session, error: aErr } = await client.auth.getUser();
  if (aErr) throw aErr;
  if (!session.user) throw new Error('not authenticated');

  const code = params.code?.trim() || randomCode(10);
  const { data, error } = await client
    .from('invites')
    .insert({
      code,
      created_by: session.user.id,
      uses_limit: params.usesLimit ?? null,
      expires_at: params.expiresAt ?? null,
      disabled: false,
    } as never)
    .select('code, created_by, uses_limit, uses_count, expires_at, disabled, created_at')
    .single();
  if (error) throw error;
  return mapInvite(data as never);
}

export async function setInviteDisabled(
  client: AppSupabaseClient,
  code: string,
  disabled: boolean,
): Promise<void> {
  const { error } = await client
    .from('invites')
    .update({ disabled } as never)
    .eq('code', code);
  if (error) throw error;
}

export async function deleteInvite(client: AppSupabaseClient, code: string): Promise<void> {
  const { error } = await client.from('invites').delete().eq('code', code);
  if (error) throw error;
}

// --- User admin ops -------------------------------------------------------

export interface AdminProfileRow {
  userId: string;
  username: string;
  displayName: string;
  isAdmin: boolean;
  banned: boolean;
  blockedFromInviting: boolean;
  createdAt: string;
}

export async function listAllProfiles(client: AppSupabaseClient): Promise<AdminProfileRow[]> {
  const { data, error } = await client
    .from('profiles')
    .select('user_id, username, display_name, is_admin, banned, blocked_from_inviting, created_at')
    .order('created_at', { ascending: false });
  if (error) throw error;
  return (data ?? []).map((r) => ({
    userId: r.user_id,
    username: r.username,
    displayName: r.display_name,
    isAdmin: r.is_admin,
    banned: r.banned,
    blockedFromInviting: r.blocked_from_inviting,
    createdAt: r.created_at,
  }));
}

export type AdminProfileFlag = 'is_admin' | 'banned' | 'blocked_from_inviting';

export async function setUserFlag(
  client: AppSupabaseClient,
  userId: string,
  flag: AdminProfileFlag,
  value: boolean,
): Promise<void> {
  const patch: Record<string, boolean> = {};
  patch[flag] = value;
  const { error } = await client
    .from('profiles')
    .update(patch as never)
    .eq('user_id', userId);
  if (error) throw error;
}

// ---------------------------------------------------------------------------
// Conversations (admin view — reads all regardless of membership)
// ---------------------------------------------------------------------------

export interface AdminConversationRow {
  id: string;
  type: 'dm' | 'group';
  name: string | null;
  memberCount: number;
  lastMessageAt: string | null;
  createdAt: string;
}

export async function listAllConversations(
  client: AppSupabaseClient,
): Promise<AdminConversationRow[]> {
  const { data, error } = await client
    .from('conversations')
    .select('id, type, name, created_at')
    .order('created_at', { ascending: false });
  if (error) throw error;
  const conversations = data ?? [];
  if (conversations.length === 0) return [];
  const ids = conversations.map((c) => c.id);
  const { data: memberRows, error: mErr } = await client
    .from('conversation_members')
    .select('conversation_id')
    .in('conversation_id', ids);
  if (mErr) throw mErr;
  const counts = new Map<string, number>();
  for (const row of memberRows ?? []) {
    counts.set(row.conversation_id, (counts.get(row.conversation_id) ?? 0) + 1);
  }
  // Approximate "last message at" by peeking at latest message.created_at per
  // conversation. Cheap since conversation count stays small (<20 users,
  // <few hundred conversations).
  const { data: lastMsg, error: lErr } = await client
    .from('messages')
    .select('conversation_id, created_at')
    .in('conversation_id', ids)
    .order('created_at', { ascending: false })
    .limit(5000);
  if (lErr) throw lErr;
  const lastAt = new Map<string, string>();
  for (const row of lastMsg ?? []) {
    if (!lastAt.has(row.conversation_id)) {
      lastAt.set(row.conversation_id, row.created_at);
    }
  }
  return conversations.map((c) => ({
    id: c.id,
    type: c.type as 'dm' | 'group',
    name: c.name,
    memberCount: counts.get(c.id) ?? 0,
    lastMessageAt: lastAt.get(c.id) ?? null,
    createdAt: c.created_at,
  }));
}

// ---------------------------------------------------------------------------
// Audit log
// ---------------------------------------------------------------------------

export interface AdminAuditEntry {
  id: string;
  actorId: string | null;
  action: string;
  targetType: string | null;
  targetId: string | null;
  metadata: Record<string, unknown>;
  createdAt: string;
}

export async function listAuditLog(
  client: AppSupabaseClient,
  limit = 100,
): Promise<AdminAuditEntry[]> {
  const { data, error } = await (client as unknown as {
    from: (t: string) => {
      select: (cols: string) => {
        order: (
          col: string,
          opts: { ascending: boolean },
        ) => {
          limit: (n: number) => Promise<{
            data:
              | {
                  id: string;
                  actor_id: string | null;
                  action: string;
                  target_type: string | null;
                  target_id: string | null;
                  metadata: Record<string, unknown>;
                  created_at: string;
                }[]
              | null;
            error: Error | null;
          }>;
        };
      };
    };
  })
    .from('admin_audit_log')
    .select('id, actor_id, action, target_type, target_id, metadata, created_at')
    .order('created_at', { ascending: false })
    .limit(limit);
  if (error) throw error;
  return (data ?? []).map((r) => ({
    id: r.id,
    actorId: r.actor_id,
    action: r.action,
    targetType: r.target_type,
    targetId: r.target_id,
    metadata: r.metadata ?? {},
    createdAt: r.created_at,
  }));
}

export async function logAdminAction(
  client: AppSupabaseClient,
  action: string,
  target?: { type?: string; id?: string },
  metadata?: Record<string, unknown>,
): Promise<void> {
  const { error } = await (client as unknown as {
    rpc: (
      name: string,
      args: Record<string, unknown>,
    ) => Promise<{ error: Error | null }>;
  }).rpc('admin_log_action', {
    p_action: action,
    p_target_type: target?.type ?? null,
    p_target_id: target?.id ?? null,
    p_metadata: metadata ?? {},
  });
  if (error) throw error;
}
