/**
 * @ai-summary User profile business logic
 * @ai-context Handles user profile operations with Auth0 integration
 */

import { UserProfileRepository } from '../data/user-profile.repository';
import {
  UserProfile,
  UserWithAdminStatus,
  UpdateProfileRequest,
  Auth0UserData,
  ListUsersQuery,
  ListUsersResponse,
  SubscriptionTier,
  DeletionStatus,
} from './user-profile.types';
import { AdminRepository } from '../../admin/data/admin.repository';
import { logger } from '../../../core/logging/logger';
import { auth0ManagementClient } from '../../../core/auth/auth0-management.client';

export class UserProfileService {
  private adminRepository: AdminRepository | null = null;

  constructor(private repository: UserProfileRepository) {}

  /**
   * Set admin repository for audit logging (optional dependency injection)
   */
  setAdminRepository(adminRepository: AdminRepository): void {
    this.adminRepository = adminRepository;
  }

  /**
   * Get or create user profile from Auth0 user data
   * This method is called when a user logs in to ensure their profile exists
   */
  async getOrCreateProfile(
    auth0Sub: string,
    auth0User: Auth0UserData
  ): Promise<UserProfile> {
    try {
      const profile = await this.repository.getOrCreate(auth0Sub, {
        email: auth0User.email,
        displayName: auth0User.name,
      });

      logger.info('User profile retrieved or created', {
        auth0Sub,
        profileId: profile.id,
      });

      return profile;
    } catch (error) {
      logger.error('Error getting or creating user profile', {
        error,
        auth0Sub,
      });
      throw error;
    }
  }

  /**
   * Get user profile by Auth0 sub
   */
  async getProfile(auth0Sub: string): Promise<UserProfile | null> {
    try {
      return await this.repository.getByAuth0Sub(auth0Sub);
    } catch (error) {
      logger.error('Error getting user profile', { error, auth0Sub });
      throw error;
    }
  }

  /**
   * Update user profile
   */
  async updateProfile(
    auth0Sub: string,
    updates: UpdateProfileRequest
  ): Promise<UserProfile> {
    try {
      // Validate that at least one field is being updated
      if (!updates.displayName && !updates.notificationEmail) {
        throw new Error('At least one field must be provided for update');
      }

      // Perform the update
      const profile = await this.repository.update(auth0Sub, updates);

      logger.info('User profile updated', {
        auth0Sub,
        profileId: profile.id,
        updatedFields: Object.keys(updates),
      });

      return profile;
    } catch (error) {
      logger.error('Error updating user profile', { error, auth0Sub, updates });
      throw error;
    }
  }

  // ============================================
  // Admin-focused methods (for user management)
  // ============================================

  /**
   * List all users with pagination and filters (admin-only)
   */
  async listAllUsers(query: ListUsersQuery): Promise<ListUsersResponse> {
    try {
      return await this.repository.listAllUsers(query);
    } catch (error) {
      logger.error('Error listing all users', { error, query });
      throw error;
    }
  }

  /**
   * Get user details with admin status (admin-only)
   */
  async getUserDetails(auth0Sub: string): Promise<UserWithAdminStatus | null> {
    try {
      return await this.repository.getUserWithAdminStatus(auth0Sub);
    } catch (error) {
      logger.error('Error getting user details', { error, auth0Sub });
      throw error;
    }
  }

  /**
   * Update user subscription tier (admin-only)
   * Logs the change to admin audit logs
   */
  async updateSubscriptionTier(
    auth0Sub: string,
    tier: SubscriptionTier,
    actorAuth0Sub: string
  ): Promise<UserProfile> {
    try {
      // Get current user to log the change
      const currentUser = await this.repository.getByAuth0Sub(auth0Sub);
      if (!currentUser) {
        throw new Error('User not found');
      }

      const previousTier = currentUser.subscriptionTier;

      // Perform the update
      const updatedProfile = await this.repository.updateSubscriptionTier(auth0Sub, tier);

      // Log to audit trail
      if (this.adminRepository) {
        await this.adminRepository.logAuditAction(
          actorAuth0Sub,
          'UPDATE_TIER',
          auth0Sub,
          'user_profile',
          updatedProfile.id,
          { previousTier, newTier: tier }
        );
      }

      logger.info('User subscription tier updated', {
        auth0Sub,
        previousTier,
        newTier: tier,
        actorAuth0Sub,
      });

      return updatedProfile;
    } catch (error) {
      logger.error('Error updating subscription tier', { error, auth0Sub, tier, actorAuth0Sub });
      throw error;
    }
  }

  /**
   * Deactivate user account (admin-only soft delete)
   * Prevents self-deactivation
   */
  async deactivateUser(
    auth0Sub: string,
    actorAuth0Sub: string,
    reason?: string
  ): Promise<UserProfile> {
    try {
      // Prevent self-deactivation
      if (auth0Sub === actorAuth0Sub) {
        throw new Error('Cannot deactivate your own account');
      }

      // Verify user exists and is not already deactivated
      const currentUser = await this.repository.getByAuth0Sub(auth0Sub);
      if (!currentUser) {
        throw new Error('User not found');
      }
      if (currentUser.deactivatedAt) {
        throw new Error('User is already deactivated');
      }

      // Perform the deactivation
      const deactivatedProfile = await this.repository.deactivateUser(auth0Sub, actorAuth0Sub);

      // Log to audit trail
      if (this.adminRepository) {
        await this.adminRepository.logAuditAction(
          actorAuth0Sub,
          'DEACTIVATE_USER',
          auth0Sub,
          'user_profile',
          deactivatedProfile.id,
          { reason: reason || 'No reason provided' }
        );
      }

      logger.info('User deactivated', {
        auth0Sub,
        actorAuth0Sub,
        reason,
      });

      return deactivatedProfile;
    } catch (error) {
      logger.error('Error deactivating user', { error, auth0Sub, actorAuth0Sub });
      throw error;
    }
  }

  /**
   * Reactivate a deactivated user account (admin-only)
   */
  async reactivateUser(
    auth0Sub: string,
    actorAuth0Sub: string
  ): Promise<UserProfile> {
    try {
      // Verify user exists and is deactivated
      const currentUser = await this.repository.getByAuth0Sub(auth0Sub);
      if (!currentUser) {
        throw new Error('User not found');
      }
      if (!currentUser.deactivatedAt) {
        throw new Error('User is not deactivated');
      }

      // Perform the reactivation
      const reactivatedProfile = await this.repository.reactivateUser(auth0Sub);

      // Log to audit trail
      if (this.adminRepository) {
        await this.adminRepository.logAuditAction(
          actorAuth0Sub,
          'REACTIVATE_USER',
          auth0Sub,
          'user_profile',
          reactivatedProfile.id,
          { previouslyDeactivatedBy: currentUser.deactivatedBy }
        );
      }

      logger.info('User reactivated', {
        auth0Sub,
        actorAuth0Sub,
      });

      return reactivatedProfile;
    } catch (error) {
      logger.error('Error reactivating user', { error, auth0Sub, actorAuth0Sub });
      throw error;
    }
  }

  /**
   * Admin update of user profile (email, displayName)
   * Logs the change to admin audit logs
   */
  async adminUpdateProfile(
    auth0Sub: string,
    updates: { email?: string; displayName?: string },
    actorAuth0Sub: string
  ): Promise<UserProfile> {
    try {
      // Get current user to log the change
      const currentUser = await this.repository.getByAuth0Sub(auth0Sub);
      if (!currentUser) {
        throw new Error('User not found');
      }

      const previousValues = {
        email: currentUser.email,
        displayName: currentUser.displayName,
      };

      // Perform the update
      const updatedProfile = await this.repository.adminUpdateProfile(auth0Sub, updates);

      // Log to audit trail
      if (this.adminRepository) {
        await this.adminRepository.logAuditAction(
          actorAuth0Sub,
          'UPDATE_PROFILE',
          auth0Sub,
          'user_profile',
          updatedProfile.id,
          {
            previousValues,
            newValues: updates,
          }
        );
      }

      logger.info('User profile updated by admin', {
        auth0Sub,
        updatedFields: Object.keys(updates),
        actorAuth0Sub,
      });

      return updatedProfile;
    } catch (error) {
      logger.error('Error admin updating user profile', { error, auth0Sub, updates, actorAuth0Sub });
      throw error;
    }
  }

  // ============================================
  // Account deletion methods (GDPR compliance)
  // ============================================

  /**
   * Request account deletion
   * Sets 30-day grace period before permanent deletion
   * Note: User is already authenticated via JWT, confirmation text is sufficient
   */
  async requestDeletion(
    auth0Sub: string,
    confirmationText: string
  ): Promise<UserProfile> {
    try {
      // Validate confirmation text
      if (confirmationText !== 'DELETE') {
        throw new Error('Invalid confirmation text');
      }

      // Get user profile
      const profile = await this.repository.getByAuth0Sub(auth0Sub);
      if (!profile) {
        throw new Error('User not found');
      }

      // Check if deletion already requested
      if (profile.deletionRequestedAt) {
        throw new Error('Deletion already requested');
      }

      // Request deletion
      const updatedProfile = await this.repository.requestDeletion(auth0Sub);

      // Log to audit trail
      if (this.adminRepository) {
        await this.adminRepository.logAuditAction(
          auth0Sub,
          'REQUEST_DELETION',
          auth0Sub,
          'user_profile',
          updatedProfile.id,
          {
            deletionScheduledFor: updatedProfile.deletionScheduledFor,
          }
        );
      }

      logger.info('Account deletion requested', {
        auth0Sub,
        deletionScheduledFor: updatedProfile.deletionScheduledFor,
      });

      return updatedProfile;
    } catch (error) {
      logger.error('Error requesting account deletion', { error, auth0Sub });
      throw error;
    }
  }

  /**
   * Cancel pending deletion request
   */
  async cancelDeletion(auth0Sub: string): Promise<UserProfile> {
    try {
      // Cancel deletion
      const updatedProfile = await this.repository.cancelDeletion(auth0Sub);

      // Log to audit trail
      if (this.adminRepository) {
        await this.adminRepository.logAuditAction(
          auth0Sub,
          'CANCEL_DELETION',
          auth0Sub,
          'user_profile',
          updatedProfile.id,
          {}
        );
      }

      logger.info('Account deletion canceled', { auth0Sub });

      return updatedProfile;
    } catch (error) {
      logger.error('Error canceling account deletion', { error, auth0Sub });
      throw error;
    }
  }

  /**
   * Get deletion status for a user profile
   */
  getDeletionStatus(profile: UserProfile): DeletionStatus {
    if (!profile.deletionRequestedAt || !profile.deletionScheduledFor) {
      return {
        isPendingDeletion: false,
        deletionRequestedAt: null,
        deletionScheduledFor: null,
        daysRemaining: null,
      };
    }

    const now = new Date();
    const scheduledDate = new Date(profile.deletionScheduledFor);
    const millisecondsRemaining = scheduledDate.getTime() - now.getTime();
    const daysRemaining = Math.ceil(millisecondsRemaining / (1000 * 60 * 60 * 24));

    return {
      isPendingDeletion: true,
      deletionRequestedAt: profile.deletionRequestedAt,
      deletionScheduledFor: profile.deletionScheduledFor,
      daysRemaining: Math.max(0, daysRemaining),
    };
  }

  /**
   * Admin hard delete user (permanent deletion)
   * Prevents self-delete
   */
  async adminHardDeleteUser(
    auth0Sub: string,
    actorAuth0Sub: string,
    reason?: string
  ): Promise<void> {
    try {
      // Prevent self-delete
      if (auth0Sub === actorAuth0Sub) {
        throw new Error('Cannot delete your own account');
      }

      // Get user profile before deletion for audit log
      const profile = await this.repository.getByAuth0Sub(auth0Sub);
      if (!profile) {
        throw new Error('User not found');
      }

      // Log to audit trail before deletion
      if (this.adminRepository) {
        await this.adminRepository.logAuditAction(
          actorAuth0Sub,
          'HARD_DELETE_USER',
          auth0Sub,
          'user_profile',
          profile.id,
          {
            reason: reason || 'No reason provided',
            email: profile.email,
            displayName: profile.displayName,
          }
        );
      }

      // Hard delete from database
      await this.repository.hardDeleteUser(auth0Sub);

      // Delete from Auth0
      await auth0ManagementClient.deleteUser(auth0Sub);

      logger.info('User hard deleted by admin', {
        auth0Sub,
        actorAuth0Sub,
        reason,
      });
    } catch (error) {
      logger.error('Error hard deleting user', { error, auth0Sub, actorAuth0Sub });
      throw error;
    }
  }
}
