import 'server-only'

import { createAdminClient } from '@/lib/supabase/admin'
import { claimImportedTrips } from '@/lib/data/imported-claims'
import { logRoleAction } from '@/lib/data/modes'
import { decideHistoricalClaim, type ClaimStatus, type ClaimDecision } from '@/lib/domain/historical-claim'
import { ROLE_LABELS, type RoleType } from '@/lib/domain/modes'

/**
 * Imported historical profiles (Nash, 2026-09-25). The rows come from
 * scripts/synchron-people; this is the only place that turns them live.
 * Requires migration 0038; without it every function answers "nothing".
 */
const missing = (e: { message?: string } | null | undefined) => !!e && /does not exist|schema cache|column/i.test(e.message ?? '')
type Admin = ReturnType<typeof createAdminClient>

interface HistoricalProfileRow { id: string; email: string | null; full_name: string | null; claim_status: ClaimStatus | null; claimed_user_id: string | null; source_system: string | null; source_record_id: string | null; historical_import: boolean }

export interface HistoricalMatch { profile: HistoricalProfileRow; roles: Array<{ roleType: RoleType; companyName: string | null; label: string }> }

/** The unclaimed / held profile behind an email, with what it would connect. Null when there is none. */
export async function findHistoricalProfile(email: string, admin: Admin = createAdminClient()): Promise<HistoricalMatch | null> {
  const normalized = email.trim().toLowerCase()
  const { data, error } = await admin.from('profiles')
    .select('id, email, full_name, claim_status, claimed_user_id, source_system, source_record_id, historical_import')
    .eq('email_normalized', normalized).eq('historical_import', true).not('claim_status', 'is', null)
    .order('created_at', { ascending: true }).limit(1)
  if (error || !data?.length) return null
  const profile = data[0] as HistoricalProfileRow
  const { data: roles } = await admin.from('membership_roles').select('role_type, company_id, status').eq('user_id', profile.id).neq('status', 'removed_by_user')
  const companyIds = [...new Set(((roles ?? []) as Array<{ company_id: string }>).map((r) => r.company_id))]
  const { data: companies } = companyIds.length ? await admin.from('companies').select('id, display_name, legal_name').in('id', companyIds) : { data: [] }
  const nameById = new Map(((companies ?? []) as Array<{ id: string; display_name: string | null; legal_name: string | null }>).map((c) => [c.id, c.display_name || c.legal_name]))
  return {
    profile,
    roles: ((roles ?? []) as Array<{ role_type: RoleType; company_id: string }>).map((r) => {
      const companyName = nameById.get(r.company_id) ?? null
      return { roleType: r.role_type, companyName, label: companyName ? `${ROLE_LABELS[r.role_type]} — ${companyName}` : ROLE_LABELS[r.role_type] }
    }),
  }
}

export interface ClaimResult { decision: ClaimDecision; connected: boolean; roles: string[]; tripIds: string[] }

/**
 * Connect the historical profile behind a VERIFIED email to the signed-in
 * account (§22). Idempotent. Callers pass emailVerified only after the email
 * code was accepted or the account is admin-provisioned — never on a plain
 * password sign-in for an unverified address.
 */
export async function claimHistoricalProfile(params: { userId: string; email: string; emailVerified: boolean; actorLabel: string }, admin: Admin = createAdminClient()): Promise<ClaimResult> {
  const none = (decision: ClaimDecision): ClaimResult => ({ decision, connected: false, roles: [], tripIds: [] })
  const match = await findHistoricalProfile(params.email, admin)
  const decision = decideHistoricalClaim({ profile: match?.profile ?? null, userId: params.userId, emailVerified: params.emailVerified })
  if (decision.action !== 'claim' && decision.action !== 'claim_repoint') return none(decision)
  const profile = match!.profile
  const now = new Date().toISOString()
  const actor = { id: params.userId, label: params.actorLabel }

  if (decision.action === 'claim_repoint') {
    // An existing account: move the imported relationships onto it. A company the account
    // already has a relationship with keeps its own row; the imported one is dropped.
    const { data: mine } = await admin.from('company_memberships').select('id, company_id').eq('user_id', params.userId)
    const ownCompanies = new Set(((mine ?? []) as Array<{ company_id: string }>).map((m) => m.company_id))
    const { data: imported } = await admin.from('company_memberships').select('id, company_id').eq('user_id', profile.id)
    for (const m of (imported ?? []) as Array<{ id: string; company_id: string }>) {
      if (ownCompanies.has(m.company_id)) { await admin.from('company_memberships').delete().eq('id', m.id); continue }
      await admin.from('company_memberships').update({ user_id: params.userId, updated_at: now }).eq('id', m.id)
    }
    await admin.from('membership_roles').update({ user_id: params.userId, updated_at: now }).eq('user_id', profile.id)
  }

  // Relationships the import left waiting for this proof (§47).
  const { data: memberships } = await admin.from('company_memberships').select('id, company_id, status')
    .eq('user_id', params.userId).eq('status', 'historical_pending_confirmation').eq('source_system', profile.source_system ?? 'synchron')
  const membershipIds = ((memberships ?? []) as Array<{ id: string }>).map((m) => m.id)
  if (membershipIds.length) {
    await admin.from('company_memberships').update({
      status: 'approved', approved_at: now, approved_by: 'HeavyHaul Agent (historical record + email verification)', confirmed_at: now,
      verification_method: 'historical_email_claim', verification_provider: 'INTERNAL_HISTORICAL_RECORDS', updated_at: now,
    }).in('id', membershipIds)
  }
  const { data: roles } = await admin.from('membership_roles').select('id, company_id, role_type, status').eq('user_id', params.userId).eq('status', 'pending').not('source_role', 'is', null)
  const roleRows = (roles ?? []) as Array<{ id: string; company_id: string; role_type: RoleType; status: string }>
  if (roleRows.length) {
    await admin.from('membership_roles').update({ status: 'active', updated_at: now }).in('id', roleRows.map((r) => r.id))
    for (const r of roleRows) await logRoleAction({ userId: params.userId, membershipRoleId: r.id, companyId: r.company_id, roleType: r.role_type, action: 'role_verified', previousStatus: 'pending', newStatus: 'active', actor, detail: { via: 'historical_email_claim', source_profile_id: profile.id } }, admin)
  }

  // Historical trips: by verified email, and by the source's own person id (§23).
  const sourceIds = profile.source_record_id ? [`user:${profile.source_record_id}`] : []
  const { tripIds } = await claimImportedTrips({ userId: params.userId, email: params.email, method: 'email_verified', actorLabel: params.actorLabel, sourceRecordIds: sourceIds })

  const { error } = await admin.from('profiles').update({
    claim_status: 'claimed', claimed_at: now, claim_method: 'email_verified', claimed_user_id: profile.id === params.userId ? null : params.userId,
    // A re-pointed profile must stop answering for this email so sign-in finds one person.
    ...(profile.id === params.userId ? {} : { email_normalized: null }),
  }).eq('id', profile.id)
  if (error && !missing(error)) console.error('historical profile claim failed', error.message)
  return { decision, connected: true, roles: match!.roles.map((r) => r.label), tripIds }
}
