import { log } from '../utils/logger';
import { API_CONFIG } from '../config/api';
import { ApiClient } from '../utils/apiClient';

interface LoginResponse {
  access_token: string;
  token_type: string;
  expires_in: number;
  user_info: {
    username: string;
    role: string;
    permissions: string[];
    full_name?: string;
    email?: string;
  };
}

interface StoredAuth {
  token: string;
  expiresAt: number;
  userInfo: LoginResponse['user_info'];
}

class AuthService {
  private static readonly AUTH_KEY = 'mcc_auth';
  private tokenCheckInterval: NodeJS.Timeout | null = null;
  private onTokenExpiredCallback: (() => void) | null = null;
  private onTokenWarningCallback: ((timeLeft: number) => void) | null = null;
  
  // Cache for synchronous access
  private currentToken: string | null = null;
  private currentUser: StoredAuth['userInfo'] | null = null;
  
  constructor() {
    // Restore auth on startup
    this.initializeAuth();
  }
  
  /**
   * Initialize auth on application startup
   * Restores token and user from safeStorage
   */
  private async initializeAuth() {
    try {
      const stored = await this.getStoredAuth();
      if (stored && Date.now() < stored.expiresAt) {
        // CRITICAL CHECK: JWT must start with "eyJ" (base64-encoded JSON header)
        // If token is invalid (encrypted blob from old version), clear storage
        if (!stored.token || !stored.token.startsWith('eyJ')) {
          log.error('auth-service', 'Invalid token format in storage, clearing auth', {
            tokenPrefix: stored.token?.substring(0, 10) || 'empty',
            tokenLength: stored.token?.length || 0
          });
          this.clearAuth();
          return;
        }
        
        this.currentToken = stored.token;
        this.currentUser = stored.userInfo;
        
        // Start token monitoring
        this.startTokenMonitoring();
        
        log.info('auth-service', 'Auth restored from storage', { 
          username: stored.userInfo.username,
          tokenPrefix: stored.token.substring(0, 20) + '...'
        });
      } else {
        log.debug('auth-service', 'No valid stored auth found');
      }
    } catch (error) {
      log.error('auth-service', 'Failed to initialize auth', { error });
      this.clearAuth();
    }
  }

  /**
   * User authentication
   */
  async login(username: string, password: string): Promise<LoginResponse> {
    try {
      log.info('auth-service', 'Starting login', { username });

      this.clearAuth();
      log.info('auth-service', 'Previous session cleared before new login');

      const data: LoginResponse = await ApiClient.post(
        `${API_CONFIG.BASE_URL}${API_CONFIG.ENDPOINTS.AUTH.LOGIN}`,
        { username, password },
        {
          timeout: 10000,
          retryConfig: { maxRetries: 2 }
        }
      );
      
      await this.saveAuth(data);
      this.startTokenMonitoring();
      
      log.info('auth-service', 'Login successful', { 
        username: data.user_info.username,
        role: data.user_info.role
      });

      return data;
    } catch (error) {
      log.error('auth-service', 'Login failed', { error });
      throw error;
    }
  }

  /**
   * Logout
   */
  async logout(): Promise<void> {
    try {
      // Use cached token
      if (this.currentToken) {
        // Use new endpoint for token revocation
        await ApiClient.post(
          `${API_CONFIG.BASE_URL}${API_CONFIG.ENDPOINTS.AUTH.REVOKE}`,
          {},
          ApiClient.withAuth(this.currentToken, {
            timeout: 5000, // Short timeout for logout
            retryConfig: { maxRetries: 1 } // Minimum retry for logout
          })
        ).catch(() => {
          // Ignore errors on logout
        });
        
        log.info('auth-service', 'Token revoked on server');
      }
    } finally {
      // Always clear local storage
      this.clearAuth();
      this.stopTokenMonitoring();
      
      log.info('auth-service', 'Logged out');
    }
  }

  /**
   * Get current token
   * FIXED: async for safeStorage support
   */
  async getToken(): Promise<string | null> {
    const stored = await this.getStoredAuth();
    if (!stored) return null;

    // Check if token expired
    if (Date.now() >= stored.expiresAt) {
      this.clearAuth();
      return null;
    }

    return stored.token;
  }

  /**
   * Get current user info (SYNCHRONOUS)
   * Used for UI - fast check without async
   */
  getCurrentUser(): StoredAuth['userInfo'] | null {
    // Check memory cache
    if (this.currentUser) {
      return this.currentUser;
    }
    return null;
  }

  /**
   * Check if user is authenticated (SYNCHRONOUS)
   * Used for UI - fast check without async
   */
  isAuthenticated(): boolean {
    return this.currentUser !== null;
  }

  /**
   * Get headers for authenticated requests (SYNCHRONOUS)
   * Used for API requests
   */
  getAuthHeaders(): Record<string, string> {
    if (!this.currentToken) {
      log.warn('auth-service', 'No token available for auth headers');
      return {};
    }
    
    // Check that token is valid before sending
    if (!this.currentToken.startsWith('eyJ')) {
      log.error('auth-service', 'Current token is invalid (not a JWT), clearing auth', {
        tokenPrefix: this.currentToken.substring(0, 10),
        tokenLength: this.currentToken.length
      });
      this.clearAuth();
      return {};
    }
    
    log.debug('auth-service', 'Providing auth headers', {
      tokenPrefix: this.currentToken.substring(0, 30) + '...'
    });
    
    return {
      'Authorization': `Bearer ${this.currentToken}`,
    };
  }
  
  /**
   * Save authentication data
   * FIXED: Uses Electron safeStorage for XSS protection
   */
  private async saveAuth(loginResponse: LoginResponse): Promise<void> {
    // Validate JWT token before saving
    if (!loginResponse.access_token || !loginResponse.access_token.startsWith('eyJ')) {
      log.error('auth-service', 'Received invalid JWT token from API', {
        tokenPrefix: loginResponse.access_token?.substring(0, 10) || 'empty'
      });
      throw new Error('Invalid JWT token received from server');
    }
    
    const auth: StoredAuth = {
      token: loginResponse.access_token,
      expiresAt: Date.now() + (loginResponse.expires_in * 1000),
      userInfo: loginResponse.user_info,
    };

    try {
      // Check Electron API availability (for dev vs prod)
      const hasElectronAPI = typeof window !== 'undefined' && 
                            window.electronAPI !== undefined &&
                            typeof window.electronAPI.isEncryptionAvailable === 'function';
      
      if (hasElectronAPI) {
        // Check safeStorage availability
        const isAvailable = await window.electronAPI.isEncryptionAvailable();
        
        if (isAvailable) {
          // SECURED: Use Electron safeStorage
          const authJson = JSON.stringify(auth);
          const encrypted = await window.electronAPI.encryptData(authJson);
          localStorage.setItem(AuthService.AUTH_KEY, encrypted);
          log.info('auth-service', 'Auth data saved securely with safeStorage');
        } else {
          // Fallback for platforms without safeStorage
          log.warn('auth-service', 'safeStorage not available, using localStorage (less secure)');
          localStorage.setItem(AuthService.AUTH_KEY, JSON.stringify(auth));
        }
      } else {
        // Dev mode: no Electron API, use regular localStorage
        log.info('auth-service', 'Running in dev mode, using localStorage');
        localStorage.setItem(AuthService.AUTH_KEY, JSON.stringify(auth));
      }
      
      // Update cache for synchronous access
      this.currentToken = auth.token;
      this.currentUser = auth.userInfo;
    } catch (error) {
      log.error('auth-service', 'Failed to save auth data', { error });
      throw error;
    }
  }

  /**
   * Get stored authentication data
   * FIXED: Decrypts data from safeStorage
   */
  private async getStoredAuth(): Promise<StoredAuth | null> {
    try {
      const stored = localStorage.getItem(AuthService.AUTH_KEY);
      if (!stored) return null;
      
      // Check Electron API availability
      const hasElectronAPI = typeof window !== 'undefined' && 
                            window.electronAPI !== undefined &&
                            typeof window.electronAPI.decryptData === 'function';
      
      // FIXED: Check if data is encrypted by first character
      // Plain JSON always starts with '{', encrypted data does not
      const isPlainJSON = stored.trim().startsWith('{');
      
      if (!isPlainJSON && hasElectronAPI) {
        try {
          // Data is encrypted - decrypt via safeStorage
          log.debug('auth-service', 'Decrypting stored auth with safeStorage');
          const decrypted = await window.electronAPI.decryptData(stored);
          return JSON.parse(decrypted);
        } catch (decryptError) {
          // Possibly corrupted data
          log.error('auth-service', 'Failed to decrypt stored auth, clearing storage', { decryptError });
          this.clearAuth();
          return null;
        }
      } else {
        // Unencrypted data (dev mode, old format or fallback)
        log.debug('auth-service', 'Loading plain JSON auth from storage');
        return JSON.parse(stored);
      }
    } catch (error) {
      log.error('auth-service', 'Failed to parse stored auth', { error });
      return null;
    }
  }

  /**
   * Clear authentication data
   */
  private clearAuth(): void {
    localStorage.removeItem(AuthService.AUTH_KEY);
    
    // Clear cache
    this.currentToken = null;
    this.currentUser = null;
    
    log.info('auth-service', 'Auth data cleared');
  }

  /**
   * Set callback for token expiration handling
   */
  setOnTokenExpired(callback: () => void): void {
    this.onTokenExpiredCallback = callback;
  }

  /**
   * Set callback for token expiration warning
   */
  setOnTokenWarning(callback: (timeLeft: number) => void): void {
    this.onTokenWarningCallback = callback;
  }

  /**
   * Start token monitoring
   */
  private startTokenMonitoring(): void {
    this.stopTokenMonitoring();
    
    // FIXED: Check token every 30 seconds with async getStoredAuth support
    this.tokenCheckInterval = setInterval(async () => {
      const stored = await this.getStoredAuth();
      if (!stored) return;

      // If token expires within next 5 minutes, notify
      const timeUntilExpiry = stored.expiresAt - Date.now();
      const fiveMinutes = 5 * 60 * 1000;

      if (timeUntilExpiry <= fiveMinutes) {
        log.warn('auth-service', 'Token expires soon', { 
          timeUntilExpiry: Math.round(timeUntilExpiry / 1000) 
        });

        if (timeUntilExpiry <= 0) {
          log.error('auth-service', 'Token expired, logging out');
          this.clearAuth();
          this.stopTokenMonitoring();
          
          if (this.onTokenExpiredCallback) {
            this.onTokenExpiredCallback();
          }
        } else if (this.onTokenWarningCallback) {
          this.onTokenWarningCallback(Math.round(timeUntilExpiry / 1000));
        }
      }
    }, 30000); // Check every 30 seconds
  }

  /**
   * Stop token monitoring
   */
  private stopTokenMonitoring(): void {
    if (this.tokenCheckInterval) {
      clearInterval(this.tokenCheckInterval);
      this.tokenCheckInterval = null;
    }
  }
}

export const authService = new AuthService();
export default authService; 