import 'server-only'

import { createAdminClient } from '@/lib/supabase/admin'
import { listEnvUsers } from '@/lib/auth/env-users'
import type { CompanyMembership, Profile, UserRole } from '@/types/db'

/**
 * Everyone the platform knows about, for the admin user console (Task 95,
 * 2026-09-09).
 *
 * Nash: "We need a page that will give us power to manage users. Let's say a
 * user needs help to reset the password… Can we assist him? What kind of
 * tools do we need in there?"
 *
 * There are two populations and support needs to tell them apart on a call:
 *   • a LOGIN — an entry in the AUTH_USERS environment variable. This is the
 *     only thing that can actually sign in.
 *   • a PROFILE — a `profiles` row. Created at sign-in, and also for anyone a
 *     trip has referenced. A profile without a login cannot sign in yet.
 *
 * Passwords live in AUTH_USERS as plain text and are never read here; the
 * EnvUser shape does not carry them. An admin-set password lives only as a
 * scrypt hash in `auth_accounts`, and only its timestamp is read here.
 */

export interface PlatformUser {
  id: string
  name: string
  email: string
  role: UserRole
  company: string | null
  phone: string | null
  /** Has an AUTH_USERS entry — i.e. can actually sign in today. */
  hasLogin: boolean
  /** Has a profiles row — i.e. exists in the database. */
  hasProfile: boolean
  username: string | null
  createdAt: string | null
  /** Trips they participate in. */
  tripCount: number
  /** An admin has set this login's password (auth_accounts). */
  passwordSetAt: string | null
  /** The role came from an admin change rather than AUTH_USERS. */
  roleOverridden: boolean
  company_membership: {
    companyId: string
    companyName: string
    role: string
    status: string
    membershipId: string
  } | null
}

function tableMissing(error: { message?: string; code?: string } | null | undefined): boolean {
  if (!error) return false
  return (
    error.code === '42P01' ||
    error.code === 'PGRST205' ||
    /does not exist|schema cache|could not find the table/i.test(error.message ?? '')
  )
}

export async function listPlatformUsers(): Promise<PlatformUser[]> {
  const admin = createAdminClient()
  const envUsers = listEnvUsers()

  const [{ data: profileRows }, { data: participantRows }, overrideRes] = await Promise.all([
    admin.from('profiles').select('*').order('created_at'),
    admin.from('trip_participants').select('user_id, email').neq('status', 'removed'),
    // Never select password_hash — only whether and when it was set.
    admin.from('auth_accounts').select('user_id, role, password_updated_at'),
  ])
  const overrides = new Map(
    ((overrideRes.error ? [] : overrideRes.data) ?? []).map(
      (o: { user_id: string; role: UserRole | null; password_updated_at: string | null }) => [o.user_id, o],
    ),
  )
  const profiles = (profileRows ?? []) as Profile[]

  // Company membership, when migration 0013 has been applied.
  const memberships = new Map<string, PlatformUser['company_membership']>()
  const mem = await admin.from('company_memberships').select('*').neq('status', 'rejected')
  if (!tableMissing(mem.error) && mem.data?.length) {
    const rows = mem.data as CompanyMembership[]
    const { data: companies } = await admin
      .from('companies')
      .select('id, display_name')
      .in('id', [...new Set(rows.map((m) => m.company_id))])
    const names = new Map(
      ((companies ?? []) as Array<{ id: string; display_name: string }>).map((c) => [c.id, c.display_name]),
    )
    for (const m of rows) {
      // An approved membership always wins over a pending one.
      const existing = memberships.get(m.user_id)
      if (existing && existing.status === 'approved') continue
      memberships.set(m.user_id, {
        companyId: m.company_id,
        companyName: names.get(m.company_id) ?? 'Unknown company',
        role: m.role,
        status: m.status,
        membershipId: m.id,
      })
    }
  }

  // Trip counts, by user id and by email (participants are linked either way).
  const tripsById = new Map<string, number>()
  const tripsByEmail = new Map<string, number>()
  for (const row of (participantRows ?? []) as Array<{ user_id: string | null; email: string }>) {
    if (row.user_id) tripsById.set(row.user_id, (tripsById.get(row.user_id) ?? 0) + 1)
    else if (row.email) {
      const key = row.email.toLowerCase()
      tripsByEmail.set(key, (tripsByEmail.get(key) ?? 0) + 1)
    }
  }

  const byId = new Map<string, PlatformUser>()

  for (const p of profiles) {
    byId.set(p.id, {
      id: p.id,
      name: p.full_name || p.email,
      email: p.email,
      role: p.default_role,
      company: p.company_name,
      phone: p.phone,
      hasLogin: false,
      hasProfile: true,
      username: null,
      createdAt: p.created_at,
      tripCount: (tripsById.get(p.id) ?? 0) + (tripsByEmail.get(p.email.toLowerCase()) ?? 0),
      company_membership: memberships.get(p.id) ?? null,
      passwordSetAt: null,
      roleOverridden: false,
    })
  }

  for (const e of envUsers) {
    const existing = byId.get(e.id)
    const override = overrides.get(e.id)
    // A login's role is the admin override when there is one, else AUTH_USERS.
    const role = override?.role ?? e.role
    if (existing) {
      existing.hasLogin = true
      existing.username = e.username
      existing.role = role
      existing.roleOverridden = !!override?.role
      existing.passwordSetAt = override?.password_updated_at ?? null
      if (!existing.company) existing.company = e.company
      if (!existing.phone) existing.phone = e.phone
      continue
    }
    byId.set(e.id, {
      id: e.id,
      name: e.name,
      email: e.email,
      role,
      company: e.company,
      phone: e.phone,
      hasLogin: true,
      hasProfile: false,
      username: e.username,
      createdAt: null,
      tripCount: (tripsById.get(e.id) ?? 0) + (tripsByEmail.get(e.email.toLowerCase()) ?? 0),
      company_membership: memberships.get(e.id) ?? null,
      passwordSetAt: override?.password_updated_at ?? null,
      roleOverridden: !!override?.role,
    })
  }

  return [...byId.values()].sort((a, b) => {
    if (a.hasLogin !== b.hasLogin) return a.hasLogin ? -1 : 1
    return a.name.localeCompare(b.name)
  })
}
