import { supabase } from '@/lib/supabase'
import type { AuthService, AuthUser } from './types'

export class SupabaseAuthService implements AuthService {
  async signInWithGoogle(): Promise<void> {
    const { error } = await supabase.auth.signInWithOAuth({
      provider: 'google',
      options: {
        redirectTo: window.location.origin,
        queryParams: {
          prompt: 'select_account', // fuerza el selector de cuentas de Google, incluso con sesión activa
        },
      },
    })
    if (error) throw error
  }

  async signInWithEmailPassword(email: string, password: string): Promise<{ error: string | null }> {
    const { error } = await supabase.auth.signInWithPassword({ email, password })
    return { error: error?.message ?? null }
  }

  async signOut(): Promise<void> {
    const { error } = await supabase.auth.signOut()
    if (error) throw error
  }

  // Auditoría Sprint 5 Etapa 1: este getSession() NO se reemplaza por getSessionSafe()
  // porque necesita el objeto session en sí (no es una barrera descartable) y getCurrentUser()
  // no tiene ningún caller en la app hoy (AuthContext usa onAuthStateChange). Cambios de auth
  // están fuera de scope de esta etapa — se documenta la decisión, no se toca el comportamiento.
  async getCurrentUser(): Promise<AuthUser | null> {
    const { data: { session } } = await supabase.auth.getSession()
    if (!session?.user) return null
    return this.buildAuthUser(session.user)
  }

  onAuthStateChange(callback: (user: AuthUser | null) => void): () => void {
    const { data: { subscription } } = supabase.auth.onAuthStateChange(
      (event, session) => {  // NO async — el lock de GoTrueClient está activo durante este callback
        try {
          // Token refresh silencioso — no disparar re-sync ni setLoading
          if (event === 'TOKEN_REFRESHED' || event === 'USER_UPDATED') return
          if (!session?.user) {
            // INITIAL_SESSION null es transitorio: Supabase no pudo verificar la sesión
            // (red lenta, cold start, timeout). Si hay sesión en localStorage, no forzar logout.
            // Solo cerrar sesión en eventos explícitos (SIGNED_OUT, USER_DELETED, etc).
            if (event !== 'INITIAL_SESSION') callback(null)
            return
          }
          // PASSWORD_RECOVERY tiene sesión activa — incluir para que AuthContext establezca el usuario
          // antes de redirigir a /reset-password. La señal de redirect se maneja en AuthContext.
          if (event !== 'INITIAL_SESSION' && event !== 'SIGNED_IN' && event !== 'PASSWORD_RECOVERY') return
          // Llamar callback inmediatamente con datos básicos del token OAuth.
          // NO hacer queries a Supabase desde aquí — el lock de auth está activo (GoTrueClient.js L3213).
          // El upsert y la lectura de platform_role/org_id ocurren en AuthContext fuera del lock.
          callback(this.buildAuthUser(session.user))
        } catch (err) {
          console.error('Error en onAuthStateChange:', err)
          if (session?.user) callback(this.buildAuthUser(session.user))
          else if (event !== 'INITIAL_SESSION') callback(null)
        }
      }
    )

    return () => subscription.unsubscribe()
  }

  // Leer platform_role, org_id, company y name desde la tabla users.
  // Sobrescribe la heurística de email de buildAuthUser con el valor real de DB.
  // Se llama desde AuthContext FUERA del lock de auth, en un useEffect normal.
  // signal permite cancelar si el servidor tarda demasiado (libera el processLock).
  async syncProfileFromDB(userId: string, signal?: AbortSignal): Promise<Pick<AuthUser, 'name' | 'company' | 'platformRole' | 'orgId'> | null> {
    const base = supabase.from('users').select('name, company, platform_role, org_id').eq('id', userId)
    const { data } = await (signal ? base.abortSignal(signal) : base).maybeSingle()
    if (!data) return null
    return {
      name: ((data.name as string | null | undefined) || undefined) as unknown as string,
      company: (data.company as string | null) ?? undefined,
      platformRole: (data.platform_role as AuthUser['platformRole']) ?? 'guest',
      orgId: (data.org_id as string | null) ?? undefined,
    }
  }

  // Upsert idempotente al login: crea la fila si no existe, ignora si ya existe.
  // Se llama desde AuthContext FUERA del lock de auth, en un useEffect normal.
  // signal permite cancelar si el servidor tarda demasiado (libera el processLock).
  async upsertUserOnLogin(user: AuthUser, signal?: AbortSignal): Promise<void> {
    const builder = supabase.from('users').upsert(
      {
        id: user.id,
        name: user.name,
        avatar_url: user.avatarUrl ?? null,
        platform_role: user.platformRole,
      },
      { onConflict: 'id', ignoreDuplicates: true }
    )
    const { error } = await (signal ? builder.abortSignal(signal) : builder)
    if (error) console.error('Error al crear usuario en DB:', error)
  }

  // Sincrónico: deriva el usuario de los metadatos de sesión sin consultar la DB.
  // El rol se deriva del dominio del email (valor optimista, syncProfileFromDB lo corrige después).
  private buildAuthUser(supabaseUser: { id: string; email?: string; user_metadata?: Record<string, unknown> }): AuthUser {
    const email = supabaseUser.email ?? ''
    return {
      id: supabaseUser.id,
      email,
      name: (supabaseUser.user_metadata?.full_name as string | undefined) || email,
      avatarUrl: supabaseUser.user_metadata?.avatar_url as string ?? undefined,
      platformRole: email.endsWith('@inquality.com.py') ? 'platform_admin' : 'guest',
    }
  }
}
