/**
 * Identity Management Service
 * Handles OAuth provider connections, disconnections, and password fallback
 */

import { supabase } from '@/integrations/supabase/client';
import type { UserIdentity as SupabaseUserIdentity } from '@supabase/supabase-js';
import type { 
  UserIdentity, 
  OAuthProvider, 
  IdentitySafetyCheck, 
  IdentityOperationResult 
} from '@/types/identity';
import { logger } from './logger';
import { getErrorMessage } from './errorHandler';

/**
 * Get all identities for the current user
 */
export async function getUserIdentities(): Promise<UserIdentity[]> {
  try {
    const { data, error } = await supabase.auth.getUserIdentities();
    
    if (error) throw error;
    
    return (data?.identities || []) as UserIdentity[];
  } catch (error) {
    const errorMsg = getErrorMessage(error);
    logger.error('Failed to get user identities', {
      action: 'get_identities',
      error: errorMsg
    });
    return [];
  }
}

/**
 * Check if user has password authentication (email provider)
 */
export async function hasPasswordAuth(): Promise<boolean> {
  const identities = await getUserIdentities();
  return identities.some(identity => identity.provider === 'email');
}

/**
 * Check if it's safe to disconnect a provider
 * Returns safety information and reason if unsafe
 */
export async function checkDisconnectSafety(
  provider: OAuthProvider
): Promise<IdentitySafetyCheck> {
  const identities = await getUserIdentities();
  
  const hasPassword = identities.some(i => i.provider === 'email');
  const oauthIdentities = identities.filter(i => 
    i.provider !== 'email' && i.provider !== 'phone'
  );
  const totalIdentities = identities.length;
  
  // Can't disconnect if it's the only identity
  if (totalIdentities === 1) {
    return {
      canDisconnect: false,
      reason: 'last_identity',
      hasPasswordAuth: hasPassword,
      totalIdentities,
      oauthIdentities: oauthIdentities.length
    };
  }
  
  // Can't disconnect last OAuth provider if no password backup
  if (oauthIdentities.length === 1 && !hasPassword) {
    return {
      canDisconnect: false,
      reason: 'no_password_backup',
      hasPasswordAuth: hasPassword,
      totalIdentities,
      oauthIdentities: oauthIdentities.length
    };
  }
  
  return {
    canDisconnect: true,
    reason: 'safe',
    hasPasswordAuth: hasPassword,
    totalIdentities,
    oauthIdentities: oauthIdentities.length
  };
}

/**
 * Disconnect an OAuth identity from the user's account
 * Requires AAL2 session for security
 */
export async function disconnectIdentity(
  provider: OAuthProvider
): Promise<IdentityOperationResult> {
  try {
    // AAL2 check for security-critical operation (MUST fail closed)
    const { data: { session } } = await supabase.auth.getSession();
    
    // Get AAL level - fail closed on error
    const { data: aalData, error: aalError } = await supabase.auth.mfa.getAuthenticatorAssuranceLevel();
    if (aalError) {
      logger.error('Failed to get AAL level for identity disconnect', {
        action: 'disconnect_identity_aal_check',
        error: aalError.message
      });
      return {
        success: false,
        error: 'Unable to verify security level. Please try again.',
        requiresAAL2: true
      };
    }
    
    const currentAal = aalData?.currentLevel || 'aal1';
    
    // If not at AAL2, check if MFA is enrolled - fail closed on error
    if (currentAal !== 'aal2') {
      const { data: factors, error: factorsError } = await supabase.auth.mfa.listFactors();
      
      if (factorsError) {
        logger.error('Failed to list MFA factors for identity disconnect', {
          action: 'disconnect_identity_mfa_check',
          error: factorsError.message
        });
        return {
          success: false,
          error: 'Unable to verify MFA status. Please try again.',
          requiresAAL2: true
        };
      }
      
      const hasEnrolledMFA = factors?.totp?.some(f => f.status === 'verified') || false;
      
      if (hasEnrolledMFA) {
        return {
          success: false,
          error: 'Please verify your identity with MFA before disconnecting accounts',
          requiresAAL2: true
        };
      }
    }
    
    // Safety check
    const safetyCheck = await checkDisconnectSafety(provider);
    if (!safetyCheck.canDisconnect) {
      return {
        success: false,
        error: safetyCheck.reason === 'last_identity'
          ? 'Cannot disconnect your only login method'
          : 'Please set a password before disconnecting your last social login'
      };
    }
    
    // Get all identities to find the one to unlink
    const identities = await getUserIdentities();
    const identity = identities.find(i => i.provider === provider);
    
    if (!identity) {
      return {
        success: false,
        error: `No ${provider} identity found`
      };
    }
    
    // Unlink the identity - cast to Supabase's expected type
    const { error } = await supabase.auth.unlinkIdentity(identity as SupabaseUserIdentity);
    
    if (error) throw error;
    
    // Log audit event
    const { data: { user } } = await supabase.auth.getUser();
    if (user) {
      await logIdentityChange(user.id, 'identity_disconnected', { provider });
    }
    
    return { success: true };
  } catch (error) {
    const errorMsg = getErrorMessage(error);
    logger.error('Failed to disconnect identity', {
      action: 'identity_disconnect',
      provider,
      error: errorMsg
    });
    return {
      success: false,
      error: errorMsg
    };
  }
}

/**
 * Connect an OAuth identity to the user's account
 */
export async function connectIdentity(
  provider: OAuthProvider,
  redirectTo?: string
): Promise<IdentityOperationResult> {
  try {
    const { error } = await supabase.auth.signInWithOAuth({
      provider,
      options: {
        redirectTo: redirectTo || `${window.location.origin}/settings?tab=security`,
        skipBrowserRedirect: false
      }
    });
    
    if (error) throw error;
    
    return { success: true };
  } catch (error) {
    const errorMsg = getErrorMessage(error);
    logger.error('Failed to connect identity', {
      action: 'identity_connect',
      provider,
      error: errorMsg
    });
    return {
      success: false,
      error: errorMsg
    };
  }
}


/**
 * Add password authentication to an OAuth-only account
 * Triggers Supabase password reset flow - user sets password via email link
 */
export async function addPasswordToAccount(): Promise<IdentityOperationResult> {
  try {
    const { data: { user } } = await supabase.auth.getUser();
    const userEmail = user?.email;
    
    if (!userEmail) {
      return {
        success: false,
        error: 'No email address found on your account'
      };
    }
    
    logger.info('Initiating password setup', {
      action: 'password_setup_initiated',
      email: userEmail
    });
    
    // Trigger Supabase password reset email
    // User clicks link and sets password, which automatically creates email identity
    const { error: resetError } = await supabase.auth.resetPasswordForEmail(
      userEmail,
      {
        redirectTo: `${window.location.origin}/auth/callback?type=recovery`
      }
    );
    
    if (resetError) {
      logger.error('Failed to send password reset email', {
        userId: user?.id,
        action: 'password_setup_email',
        error: resetError.message
      });
      throw resetError;
    }
    
    logger.info('Password reset email sent', {
      userId: user!.id,
      action: 'password_setup_initiated',
      email: userEmail
    });
    
    // Log the action
    await logIdentityChange(user!.id, 'password_setup_initiated', { 
      method: 'reset_password_flow',
      timestamp: new Date().toISOString()
    });
    
    return { 
      success: true,
      needsEmailConfirmation: true,
      email: userEmail
    };
  } catch (error) {
    const errorMsg = getErrorMessage(error);
    logger.error('Failed to initiate password setup', {
      action: 'password_setup',
      error: errorMsg
    });
    return {
      success: false,
      error: errorMsg
    };
  }
}


/**
 * Log identity changes to audit log
 */
async function logIdentityChange(
  userId: string,
  action: string,
  details: Record<string, any>
): Promise<void> {
  try {
    await supabase.rpc('log_admin_action', {
      _admin_user_id: userId,
      _target_user_id: userId,
      _action: action,
      _details: details
    });
  } catch (error) {
    logger.error('Failed to log identity change to audit', {
      userId,
      action,
      error: error instanceof Error ? error.message : String(error)
    });
    // Don't fail the operation if audit logging fails
  }
}
