import { effectiveIdentity } from '@/lib/domain/account'
import { surfacesForEmail } from '@/lib/data/moderator-access'
import 'server-only'

import { redirect } from 'next/navigation'
import { readSession } from '@/lib/auth/session'
import { effectiveRole, getAccountOverride, isSessionRevoked } from '@/lib/auth/accounts'
import { findEnvUserById } from '@/lib/auth/env-users'
import { findSelfAccountById, selfAccountAsEnvUser } from '@/lib/auth/self-accounts'
import { isInternalRole } from '@/lib/domain/roles'
import { ACCESS_STATUSES } from '@/lib/domain/participants'
import { createAdminClient } from '@/lib/supabase/admin'
import type { Profile, TripParticipant, UserRole } from '@/types/db'

/** Signed-in user resolved from the .env-auth session cookie. */
export interface SessionUser {
  id: string
  email: string
  name: string
  role: UserRole
  company: string | null
  /** Signed in as an admin — gates pilot/preview tooling. */
  internal: boolean
  /** The account that actually signed in (differs during a pilot role switch). */
  originId: string
  /** Moderator pages this person may open (migration 0032). Absent for a customer; an admin needs none. */
  surfaces?: string[]
  /** The active mode's context key (migration 0034), validated on every request. */
  contextKey?: string | null
  /** Admin only: the product view being previewed. */
  previewWorkspace?: string | null
}

export async function getSessionUser(): Promise<SessionUser | null> {
  const session = await readSession()
  if (!session) return null
  // A password reset or role change made by an admin ends every session
  // issued before it (2026-09-11) — otherwise a reset would not lock out an
  // old session, and a demoted admin would keep admin for up to 7 days.
  if (await isSessionRevoked(session.iat, [session.sub, session.origin_sub])) return null
  const originId = session.origin_sub ?? session.sub

  // The role and the admin tooling are resolved LIVE from AUTH_USERS plus the
  // admin overrides, not from what the cookie froze at sign-in. A cookie
  // lasts 7 days; when Nash_Turcan was promoted to admin in the env, the old
  // cookie kept saying "broker" and hid the users / moderation / templates /
  // company-review pages and the pilot bar until a manual sign-out. An
  // account removed from AUTH_USERS loses its session at once.
  // Env login first; otherwise a self-service account (verified email, 2026-09-22).
  let env = findEnvUserById(session.sub)
  if (!env) {
    const self = await findSelfAccountById(session.sub)
    if (!self || self.blocked_at) return null
    env = selfAccountAsEnvUser(self)
  }
  const override = await getAccountOverride(env.id)
  const role = effectiveRole(env, override)
  // A later role promotion must not turn a passwordless customer session into
  // an admin session; internal accounts use the existing password + MFA path.
  if (session.auth_method && isInternalRole(role)) return null
  // Contact details the person changed themselves (2026-09-23) — live, not what the cookie froze.
  env = effectiveIdentity(env, override)

  // Admin powers follow whoever actually SIGNED IN: an admin who switched
  // into the driver test account keeps the pilot bar that switches them back.
  let internal = isInternalRole(role)
  if (!internal && originId !== session.sub) {
    const originEnv = findEnvUserById(originId)
    internal = !!originEnv && isInternalRole(effectiveRole(originEnv, await getAccountOverride(originEnv.id)))
  }

  // Per-page moderator access (2026-09-23). An admin already sees every page, so the
  // lookup only runs for everyone else; it is cached for 30 seconds.
  const surfaces = role === 'admin' ? [] : await surfacesForEmail(env.email)

  return {
    id: session.sub,
    email: env.email,
    name: env.name || session.name,
    role,
    company: session.company ?? null,
    internal,
    originId,
    surfaces,
    contextKey: session.ctx ?? null,
    previewWorkspace: session.preview ?? null,
  }
}

export async function requireUser(): Promise<SessionUser> {
  const user = await getSessionUser()
  if (!user) redirect('/login')
  return user
}

export async function getProfile(): Promise<Profile | null> {
  const user = await getSessionUser()
  if (!user) return null
  const admin = createAdminClient()
  const { data } = await admin.from('profiles').select('*').eq('id', user.id).maybeSingle()
  if (data) return data as Profile
  // Fall back to the session itself (profile rows are upserted at sign-in).
  return {
    id: user.id,
    email: user.email,
    full_name: user.name,
    phone: null,
    company_name: user.company,
    default_role: user.role,
    created_at: new Date().toISOString(),
  }
}

/**
 * The caller's participant row on a trip (matches by user id or email).
 * Only rows that grant access count — an `imported` row from a historical
 * order is not "my participation" until the person has claimed it, so the
 * API guard built on this keeps historical trips closed (2026-09-14).
 */
export async function getMyParticipant(tripId: string): Promise<TripParticipant | null> {
  const user = await getSessionUser()
  if (!user) return null
  const admin = createAdminClient()
  const { data } = await admin
    .from('trip_participants')
    .select('*')
    .eq('trip_id', tripId)
    .in('status', [...ACCESS_STATUSES])
  const rows = (data ?? []) as TripParticipant[]
  return (
    rows.find((p) => p.user_id === user.id) ??
    rows.find((p) => p.email.toLowerCase() === user.email.toLowerCase()) ??
    null
  )
}
