import 'server-only'

import { createAdminClient } from '@/lib/supabase/admin'
import { sendPlatformEmail } from '@/lib/email/send'
import { SITE_URL } from '@/lib/app-url'
import { emailPattern } from '@/lib/like'
import { logSecurityEvent } from '@/lib/security/events'
import { isDelegatable, surfaceByKey, validateModeratorEmail, type SurfaceKey } from '@/lib/domain/moderator-access'

/**
 * Per-page moderator access — the I/O (Nash, 2026-09-23). Rules in
 * src/lib/domain/moderator-access.ts. Needs migration 0032; without it
 * `moderatorAccessAvailable` is false, nobody but the platform admin gets in
 * and the access card says which migration to run.
 */

type Admin = ReturnType<typeof createAdminClient>
const missing = (e: { message?: string; code?: string } | null | undefined) =>
  !!e && (e.code === '42P01' || e.code === 'PGRST205' || /does not exist|schema cache|could not find the table/i.test(e.message ?? ''))

export interface ModeratorGrant {
  id: string
  email: string
  email_normalized: string
  name: string | null
  surface: SurfaceKey
  user_id: string | null
  granted_by: string
  granted_at: string
  invitation_status: string
  accepted_at: string | null
  last_access_at: string | null
  revoked_at: string | null
  revoked_by: string | null
  note: string | null
}

export interface AccessLogRow { id: string; surface: string; action: string; actor_label: string; email_normalized: string; detail: Record<string, unknown> | null; created_at: string }

export interface Actor { id: string | null; label: string; email: string; ip: string | null }

export async function moderatorAccessAvailable(admin: Admin = createAdminClient()): Promise<boolean> {
  const { error } = await admin.from('moderator_grants').select('id').limit(1)
  return !missing(error)
}

/* ------------------------------------------------------- session lookup */

/**
 * Surface keys this address may open. Cached briefly because it runs on every
 * request through getSessionUser — a grant or revoke clears the entry at once,
 * so a change takes effect immediately in this process and within 30 seconds
 * across several.
 */
const TTL_MS = 30_000
const cache = new Map<string, { value: string[]; expires: number }>()

export function invalidateSurfaces(email: string) {
  cache.delete(email.trim().toLowerCase())
}
export function invalidateAllSurfaces() {
  cache.clear()
}

export async function surfacesForEmail(email: string | null | undefined): Promise<string[]> {
  const key = (email ?? '').trim().toLowerCase()
  if (!key) return []
  const hit = cache.get(key)
  if (hit && hit.expires > Date.now()) return hit.value
  // A database that cannot answer grants nothing — access fails closed — and a
  // failure here must never throw: this runs inside getSessionUser on every request.
  try {
    const { data, error } = await createAdminClient().from('moderator_grants').select('surface').eq('email_normalized', key).is('revoked_at', null)
    const value = error ? [] : [...new Set(((data ?? []) as Array<{ surface: string }>).map((r) => r.surface))].filter(isDelegatable)
    if (!error) cache.set(key, { value, expires: Date.now() + TTL_MS })
    return value
  } catch {
    return []
  }
}

/** First time a granted person signs in: link the account and stamp acceptance. */
export async function linkModeratorGrants(user: { id: string; email: string; name?: string | null }): Promise<void> {
  const email = user.email.trim().toLowerCase()
  if (!email) return
  const admin = createAdminClient()
  const { data, error } = await admin.from('moderator_grants').select('id, accepted_at, user_id, surface').eq('email_normalized', email).is('revoked_at', null)
  if (error || !data?.length) return
  const now = new Date().toISOString()
  for (const g of data as Array<{ id: string; accepted_at: string | null; user_id: string | null; surface: string }>) {
    if (g.accepted_at && g.user_id === user.id) continue
    await admin.from('moderator_grants').update({ user_id: user.id, accepted_at: g.accepted_at ?? now, last_access_at: now, name: user.name || undefined, updated_at: now }).eq('id', g.id)
    if (!g.accepted_at) {
      await admin.from('moderator_access_log').insert({ grant_id: g.id, email_normalized: email, surface: g.surface, action: 'accepted', actor_label: user.name || email, actor_user_id: user.id })
    }
  }
  invalidateSurfaces(email)
}

/* --------------------------------------------------------------- lists */

export async function listGrants(surface: SurfaceKey, admin: Admin = createAdminClient()): Promise<ModeratorGrant[]> {
  const { data, error } = await admin.from('moderator_grants').select('*').eq('surface', surface).order('granted_at', { ascending: false })
  if (error) return []
  return (data ?? []) as ModeratorGrant[]
}

export async function listAllGrants(admin: Admin = createAdminClient()): Promise<ModeratorGrant[]> {
  const { data, error } = await admin.from('moderator_grants').select('*').order('granted_at', { ascending: false })
  if (error) return []
  return (data ?? []) as ModeratorGrant[]
}

export async function listAccessLog(surface: SurfaceKey, limit = 20, admin: Admin = createAdminClient()): Promise<AccessLogRow[]> {
  const { data, error } = await admin.from('moderator_access_log').select('*').eq('surface', surface).order('created_at', { ascending: false }).limit(limit)
  if (error) return []
  return (data ?? []) as AccessLogRow[]
}

/* -------------------------------------------------------------- grant */

export async function grantSurface(params: {
  surface: SurfaceKey
  email: string
  name?: string | null
  note?: string | null
  actor: Actor
  origin?: string
}): Promise<{ ok: true; grant: ModeratorGrant; emailStatus: string } | { ok: false; error: string }> {
  const surface = surfaceByKey(params.surface)
  if (!surface) return { ok: false, error: 'Unknown page.' }
  if (surface.adminOnly) return { ok: false, error: `${surface.label} cannot be delegated — it manages accounts and roles.` }
  const checked = validateModeratorEmail(params.email)
  if (!checked.ok) return { ok: false, error: checked.error }
  const email = checked.email
  if (email === params.actor.email.trim().toLowerCase()) return { ok: false, error: 'That is you — you already have access to every page.' }

  const admin = createAdminClient()
  const now = new Date().toISOString()
  const name = params.name?.trim() || null
  // Someone already on the platform keeps their account; a new address is linked on first sign-in.
  const { data: prof } = await admin.from('profiles').select('id, full_name').ilike('email', emailPattern(email)).maybeSingle()
  const existingUser = (prof ?? null) as { id: string; full_name: string | null } | null

  const row = {
    email: params.email.trim(), email_normalized: email, name: name ?? existingUser?.full_name ?? null, surface: params.surface,
    user_id: existingUser?.id ?? null, granted_by: params.actor.label, granted_by_user_id: params.actor.id,
    granted_at: now, revoked_at: null, revoked_by: null, note: params.note?.trim() || null, updated_at: now,
  }
  const { data, error } = await admin.from('moderator_grants').upsert(row, { onConflict: 'email_normalized,surface' }).select('*').single()
  if (error) return { ok: false, error: missing(error) ? 'Moderator access needs database migration 0032.' : error.message }
  const grant = data as ModeratorGrant

  const r = await sendPlatformEmail({
    templateKey: 'moderator_access_invitation', to: params.email.trim(), userId: existingUser?.id ?? null, actorUserId: params.actor.id, origin: params.origin,
    data: {
      user_name: grant.name || params.email.trim(), user_email: params.email.trim(), page_name: surface.label, page_description: surface.description,
      page_link: `${params.origin ?? SITE_URL}${surface.path}`, granted_by: params.actor.label, access_note: grant.note ?? '',
      sign_in_link: `${params.origin ?? SITE_URL}/login`, has_account: existingUser ? 'yes' : '',
    },
    tags: { kind: 'moderator_access', surface: params.surface },
  })
  await admin.from('moderator_grants').update({ invitation_status: r.status === 'sent' || r.status === 'recorded_not_delivered' ? 'sent' : r.status === 'failed' ? 'failed' : 'suppressed', updated_at: now }).eq('id', grant.id)
  await admin.from('moderator_access_log').insert({
    grant_id: grant.id, email_normalized: email, surface: params.surface, action: 'granted', actor_label: params.actor.label, actor_user_id: params.actor.id, ip: params.actor.ip,
    detail: { page: surface.label, email_status: r.status, had_account: !!existingUser, note: grant.note },
  })
  await logSecurityEvent({
    event: 'admin_role_change', username: email, userId: existingUser?.id ?? null, actorUserId: params.actor.id, actorLabel: params.actor.label, ip: params.actor.ip,
    detail: { kind: 'moderator_access_granted', surface: params.surface, page: surface.label },
  })
  invalidateSurfaces(email)
  return { ok: true, grant, emailStatus: r.status }
}

export async function revokeSurface(params: { grantId: string; actor: Actor }): Promise<{ ok: true; email: string; surface: string } | { ok: false; error: string }> {
  const admin = createAdminClient()
  const { data: existing } = await admin.from('moderator_grants').select('*').eq('id', params.grantId).maybeSingle()
  const grant = (existing ?? null) as ModeratorGrant | null
  if (!grant) return { ok: false, error: 'That access record no longer exists.' }
  if (grant.revoked_at) return { ok: false, error: 'That access is already revoked.' }
  const now = new Date().toISOString()
  const { error } = await admin.from('moderator_grants').update({ revoked_at: now, revoked_by: params.actor.label, updated_at: now }).eq('id', params.grantId)
  if (error) return { ok: false, error: error.message }
  await admin.from('moderator_access_log').insert({
    grant_id: grant.id, email_normalized: grant.email_normalized, surface: grant.surface, action: 'revoked', actor_label: params.actor.label, actor_user_id: params.actor.id, ip: params.actor.ip,
    detail: { page: surfaceByKey(grant.surface)?.label ?? grant.surface },
  })
  await logSecurityEvent({
    event: 'admin_role_change', username: grant.email_normalized, userId: grant.user_id, actorUserId: params.actor.id, actorLabel: params.actor.label, ip: params.actor.ip,
    detail: { kind: 'moderator_access_revoked', surface: grant.surface },
  })
  invalidateSurfaces(grant.email_normalized)
  return { ok: true, email: grant.email_normalized, surface: grant.surface }
}

/** Send the invitation again for an existing grant (they lost the email). */
export async function reinviteSurface(params: { grantId: string; actor: Actor; origin?: string }): Promise<{ ok: true; emailStatus: string } | { ok: false; error: string }> {
  const admin = createAdminClient()
  const { data } = await admin.from('moderator_grants').select('*').eq('id', params.grantId).maybeSingle()
  const grant = (data ?? null) as ModeratorGrant | null
  if (!grant) return { ok: false, error: 'That access record no longer exists.' }
  if (grant.revoked_at) return { ok: false, error: 'That access is revoked — grant it again instead.' }
  const surface = surfaceByKey(grant.surface)
  if (!surface) return { ok: false, error: 'Unknown page.' }
  const r = await sendPlatformEmail({
    templateKey: 'moderator_access_invitation', to: grant.email, userId: grant.user_id, actorUserId: params.actor.id, origin: params.origin,
    data: {
      user_name: grant.name || grant.email, user_email: grant.email, page_name: surface.label, page_description: surface.description,
      page_link: `${params.origin ?? SITE_URL}${surface.path}`, granted_by: params.actor.label, access_note: grant.note ?? '',
      sign_in_link: `${params.origin ?? SITE_URL}/login`, has_account: grant.user_id ? 'yes' : '',
    },
    tags: { kind: 'moderator_access', surface: grant.surface },
  })
  await admin.from('moderator_access_log').insert({ grant_id: grant.id, email_normalized: grant.email_normalized, surface: grant.surface, action: 'reinvited', actor_label: params.actor.label, actor_user_id: params.actor.id, ip: params.actor.ip, detail: { email_status: r.status } })
  return { ok: true, emailStatus: r.status }
}

/** Record that a moderator opened one of their pages (shown on the access card). */
export async function touchAccess(email: string, surface: SurfaceKey): Promise<void> {
  const admin = createAdminClient()
  await admin.from('moderator_grants').update({ last_access_at: new Date().toISOString() }).eq('email_normalized', email.trim().toLowerCase()).eq('surface', surface).is('revoked_at', null)
}
