
import { IUser, IRole } from '../types';
import { AppError, ErrorCodes } from '../utils/AppError';
import { apiClient } from './apiClient';
import { dataService } from './dataService';

export class AuthService {
  private rolesCache: IRole[] | null = null;
  private rolesCacheTime: number = 0;
  private readonly ROLES_CACHE_TTL = 5 * 60 * 1000; // 5 minutes

  async getCurrentUser(): Promise<IUser | null> {
    // Check if token exists before making API call to avoid unnecessary 401 errors
    const token = apiClient.getToken();
    if (!token) {
      return null;
    }

    try {
      const response = await apiClient.get<IUser>('/auth/me');
      if (response.success && response.data) {
        return response.data;
      }
      // 401 or other errors - user not authenticated, this is normal
      return null;
    } catch (error: any) {
      // Don't log 401 errors as they are expected when user is not logged in
      if (!error.message?.includes('Unauthorized') && !error.message?.includes('401')) {
        console.error('Failed to get current user:', error);
      }
      return null;
    }
  }

  private async getRoles(): Promise<IRole[]> {
    // Use cache if still valid
    const now = Date.now();
    if (this.rolesCache && (now - this.rolesCacheTime) < this.ROLES_CACHE_TTL) {
      return this.rolesCache;
    }

    try {
      const roles = await dataService.getRoles();
      this.rolesCache = roles;
      this.rolesCacheTime = now;
      return roles;
    } catch (error) {
      console.error('Failed to load roles:', error);
      // Return cached roles if available, even if expired
      return this.rolesCache || [];
    }
  }

  private getUserRoleCode(user: IUser): string | null {
    if (!user.role) return null;
    // Try to find role by name
    const role = this.rolesCache?.find(r => r.name === user.role && r.isActive);
    return role?.code || null;
  }

  async hasPermission(userId: string, permission: string): Promise<boolean> {
    try {
      const user = await this.getCurrentUser();
      if (!user || user._id !== userId) return false;

      // Load roles if not cached
      await this.getRoles();

      // Check if user is admin (by role code ADMIN)
      const userRoleCode = this.getUserRoleCode(user);
      if (userRoleCode === 'ADMIN') return true;

      // Find user's role and check permissions
      const userRole = this.rolesCache?.find(r => r.name === user.role && r.isActive);
      if (!userRole) return false;

      // Check if permission exists in role's permissions
      return userRole.permissions?.[permission] === true || false;
    } catch (error) {
      console.error('Error checking permission:', error);
      return false;
    }
  }

  // Helper method to check if user has a specific role code
  async userHasRoleCode(user: IUser, roleCode: string): Promise<boolean> {
    await this.getRoles();
    return this.getUserRoleCode(user) === roleCode;
  }

  // Helper method to check if user has any of the specified role codes
  async userHasAnyRoleCode(user: IUser, roleCodes: string[]): Promise<boolean> {
    await this.getRoles();
    const userRoleCode = this.getUserRoleCode(user);
    return userRoleCode ? roleCodes.includes(userRoleCode) : false;
  }

  async login(email: string, password: string): Promise<{ token: string, user: IUser }> {
    try {
      const response = await apiClient.post<{ token: string, user: IUser }>('/auth/login', {
        email,
        password,
      });

      if (response.success && response.data) {
        // Store token
        const token = response.data.token;
        console.log('[AuthService] Login successful, setting token:', {
          tokenLength: token?.length || 0,
          tokenPreview: token ? `${token.substring(0, 20)}...` : 'none',
          hasUser: !!response.data.user
        });
        apiClient.setToken(token);
        
        // Verify token was stored
        const storedToken = apiClient.getToken();
        if (!storedToken || storedToken !== token) {
          console.error('[AuthService] Token storage verification failed:', {
            expected: token?.substring(0, 20),
            actual: storedToken?.substring(0, 20) || 'none'
          });
        } else {
          console.log('[AuthService] Token storage verified successfully');
        }
        
        return response.data;
      }

      throw new AppError(
        response.message || 'Login failed',
        401,
        ErrorCodes.UNAUTHORIZED
      );
    } catch (error: any) {
      console.error('[AuthService] Login error:', error);
      if (error instanceof AppError) {
        throw error;
      }
      throw new AppError(
        error.message || 'Login failed',
        401,
        ErrorCodes.UNAUTHORIZED
      );
    }
  }

  async logout(): Promise<void> {
    try {
      await apiClient.post('/auth/logout');
    } catch (error) {
      console.error('Logout error:', error);
    } finally {
      // Clear token regardless of API call success
      apiClient.setToken(null);
    }
  }
}
