import { createClient, type SupabaseClient } from '@supabase/supabase-js'
import type { Database } from '../../database.types'
import type {
  AuthProvider,
  AuthCredentials,
  SignUpCredentials,
  AuthResult,
  Session,
  User,
  AuthError,
  AuthState,
  UserRole,
} from '../types'

export class SupabaseAuthProvider implements AuthProvider {
  private client: SupabaseClient<Database>
  private listeners: ((state: AuthState) => void)[] = []

  constructor(url: string, anonKey: string) {
    this.client = createClient<Database>(url, anonKey, {
      auth: {
        flowType: 'pkce',
        autoRefreshToken: true,
        persistSession: true,
        detectSessionInUrl: false,
      },
    })

    this.client.auth.onAuthStateChange((event, session) => {
      this.notifyListeners(this.createAuthState(session))
    })
  }

  async signIn(credentials: AuthCredentials): Promise<AuthResult> {
    try {
      const { data, error } = await this.client.auth.signInWithPassword({
        email: credentials.email,
        password: credentials.password,
      })

      if (error) {
        return {
          user: null,
          session: null,
          error: this.mapSupabaseError(error),
        }
      }

      const user = await this.enrichUser(data.user)
      const session = this.mapSession(data.session, user)

      return {
        user,
        session,
        error: null,
      }
    } catch (error) {
      return {
        user: null,
        session: null,
        error: this.mapError(error),
      }
    }
  }

  async signUp(credentials: SignUpCredentials): Promise<AuthResult> {
    try {
      const { data, error } = await this.client.auth.signUp({
        email: credentials.email,
        password: credentials.password,
        options: {
          data: {
            organization_id: credentials.organizationId,
          },
        },
      })

      if (error) {
        return {
          user: null,
          session: null,
          error: this.mapSupabaseError(error),
        }
      }

      const user = data.user ? await this.enrichUser(data.user) : null
      const session = data.session && user ? this.mapSession(data.session, user) : null

      return {
        user,
        session,
        error: null,
      }
    } catch (error) {
      return {
        user: null,
        session: null,
        error: this.mapError(error),
      }
    }
  }

  async signOut(): Promise<void> {
    try {
      await this.client.auth.signOut()
    } catch (error) {
      throw new Error('Failed to sign out')
    }
  }

  async getSession(): Promise<Session | null> {
    try {
      const { data: { session }, error } = await this.client.auth.getSession()
      
      if (error || !session) {
        return null
      }

      const user = await this.enrichUser(session.user)
      return this.mapSession(session, user)
    } catch (error) {
      return null
    }
  }

  async refreshSession(): Promise<Session | null> {
    try {
      const { data: { session }, error } = await this.client.auth.refreshSession()
      
      if (error || !session) {
        return null
      }

      const user = await this.enrichUser(session.user)
      return this.mapSession(session, user)
    } catch (error) {
      return null
    }
  }

  async resetPassword(email: string): Promise<void> {
    try {
      const { error } = await this.client.auth.resetPasswordForEmail(email, {
        redirectTo: `${window.location.origin}/reset-password`,
      })

      if (error) {
        throw new Error(error.message)
      }
    } catch (error) {
      throw new Error('Failed to send reset password email')
    }
  }

  async updatePassword(password: string): Promise<void> {
    try {
      const { error } = await this.client.auth.updateUser({ password })

      if (error) {
        throw new Error(error.message)
      }
    } catch (error) {
      throw new Error('Failed to update password')
    }
  }

  onAuthStateChange(callback: (state: AuthState) => void): () => void {
    this.listeners.push(callback)
    
    return () => {
      const index = this.listeners.indexOf(callback)
      if (index > -1) {
        this.listeners.splice(index, 1)
      }
    }
  }

  private async enrichUser(supabaseUser: any): Promise<User> {
    const { data: userData, error } = await this.client
      .from('users')
      .select('*')
      .eq('id', supabaseUser.id)
      .single()

    const userRole: UserRole = userData?.is_super_admin 
      ? 'super_admin' 
      : userData?.is_admin 
        ? 'admin' 
        : userData?.is_territory_manager 
          ? 'territory_manager' 
          : 'user'

    return {
      id: supabaseUser.id,
      email: supabaseUser.email,
      emailVerified: supabaseUser.email_confirmed_at !== null,
      createdAt: supabaseUser.created_at,
      updatedAt: supabaseUser.updated_at,
      organizationId: userData?.organization_id,
      roleType: userRole,
      isActive: userData?.is_active ?? true,
      lastLoginAt: supabaseUser.last_sign_in_at,
      metadata: supabaseUser.user_metadata,
    }
  }

  private mapSession(supabaseSession: any, user: User): Session {
    return {
      accessToken: supabaseSession.access_token,
      refreshToken: supabaseSession.refresh_token,
      expiresAt: supabaseSession.expires_at,
      user,
    }
  }

  private mapSupabaseError(error: any): AuthError {
    const errorMap: Record<string, string> = {
      'invalid_credentials': 'Invalid email or password',
      'email_not_confirmed': 'Please confirm your email address',
      'user_not_found': 'User not found',
      'weak_password': 'Password is too weak',
      'signup_disabled': 'Sign up is currently disabled',
      'email_address_invalid': 'Invalid email address',
      'password_too_short': 'Password must be at least 6 characters',
    }

    return {
      code: error.message || 'unknown_error',
      message: errorMap[error.message] || error.message || 'An error occurred',
      details: error,
    }
  }

  private mapError(error: any): AuthError {
    return {
      code: 'unknown_error',
      message: error.message || 'An unexpected error occurred',
      details: error,
    }
  }

  private createAuthState(session: any): AuthState {
    return {
      user: session?.user || null,
      session: session || null,
      isLoading: false,
      isAuthenticated: !!session,
      error: null,
    }
  }

  private notifyListeners(state: AuthState): void {
    this.listeners.forEach(listener => listener(state))
  }
}

export function createSupabaseAuthProvider(url: string, anonKey: string): SupabaseAuthProvider {
  return new SupabaseAuthProvider(url, anonKey)
}