import { Injectable } from '@angular/core';
import { environment } from '../../../environments/environment';
import { TokenValidator } from '../utils/token-validator';

/**
 * Secure Storage Service
 *
 * Security considerations:
 * - Uses sessionStorage for tokens (cleared when browser closes)
 * - localStorage for non-sensitive user info only
 * - Validates token format before storage
 * - Sanitizes stored data
 *
 * Note: For maximum security, tokens should be stored in httpOnly cookies
 * set by the backend. This client-side storage is defense-in-depth.
 */
@Injectable({
  providedIn: 'root',
})
export class StorageService {
  // Use sessionStorage for tokens (more secure - cleared on browser close)
  private readonly tokenStorage = sessionStorage;
  // Use localStorage for non-sensitive data that should persist
  private readonly persistentStorage = localStorage;

  /**
   * Get token with validation
   */
  getToken(): string | null {
    console.log('[STORAGE DEBUG] getToken called, key:', environment.tokenStorageKey);
    const token = this.tokenStorage.getItem(environment.tokenStorageKey);
    console.log('[STORAGE DEBUG] Raw token from storage:', token ? `${token.substring(0, 30)}...` : 'NULL');

    // Validate token format before returning
    if (token && !TokenValidator.isValidFormat(token)) {
      console.warn('[STORAGE DEBUG] Invalid token format detected, clearing...');
      this.removeToken();
      return null;
    }

    // Check if token is expired (with 60 second buffer for clock skew)
    if (token && TokenValidator.isExpired(token, 60)) {
      console.warn('[STORAGE DEBUG] Token expired, clearing...');
      this.removeToken();
      return null;
    }

    return token;
  }

  /**
   * Set token with validation
   */
  setToken(token: string): void {
    console.log('[STORAGE DEBUG] setToken called, token length:', token?.length);
    if (!token || typeof token !== 'string') {
      console.error('[STORAGE DEBUG] Invalid token provided');
      return;
    }

    // Validate token format
    if (!TokenValidator.isValidFormat(token)) {
      console.error('[STORAGE DEBUG] Token format validation failed');
      return;
    }

    // Note: We don't check expiration here because the token just came from the server.
    // Clock skew between client and server could cause valid tokens to appear expired.
    // Expiration is checked when retrieving the token instead.

    console.log('[STORAGE DEBUG] Storing token with key:', environment.tokenStorageKey);
    this.tokenStorage.setItem(environment.tokenStorageKey, token);
    console.log('[STORAGE DEBUG] Token stored, sessionStorage contents:', Object.keys(sessionStorage));
  }

  removeToken(): void {
    this.tokenStorage.removeItem(environment.tokenStorageKey);
  }

  /**
   * Check if token should be refreshed
   */
  shouldRefreshToken(): boolean {
    const token = this.tokenStorage.getItem(environment.tokenStorageKey);
    if (!token) return false;
    return TokenValidator.shouldRefresh(token, 300); // 5 minutes before expiry
  }

  /**
   * Get token expiration time
   */
  getTokenExpiry(): Date | null {
    const token = this.tokenStorage.getItem(environment.tokenStorageKey);
    if (!token) return null;
    return TokenValidator.getExpirationDate(token);
  }

  getRefreshToken(): string | null {
    return this.tokenStorage.getItem(environment.refreshTokenStorageKey);
  }

  setRefreshToken(token: string): void {
    if (!token || typeof token !== 'string') {
      return;
    }
    this.tokenStorage.setItem(environment.refreshTokenStorageKey, token);
  }

  removeRefreshToken(): void {
    this.tokenStorage.removeItem(environment.refreshTokenStorageKey);
  }

  /**
   * Get user with sanitization
   */
  getUser<T>(): T | null {
    console.log('[STORAGE DEBUG] getUser called, key:', environment.userStorageKey);
    const user = this.persistentStorage.getItem(environment.userStorageKey);
    console.log('[STORAGE DEBUG] Raw user from storage:', user ? user.substring(0, 50) + '...' : 'NULL');
    if (user) {
      try {
        const parsed = JSON.parse(user) as T;
        // Basic validation - ensure it's an object
        if (typeof parsed !== 'object' || parsed === null) {
          this.removeUser();
          return null;
        }
        return parsed;
      } catch {
        // Invalid JSON - clear corrupted data
        this.removeUser();
        return null;
      }
    }
    return null;
  }

  /**
   * Set user with sanitization
   */
  setUser<T>(user: T): void {
    console.log('[STORAGE DEBUG] setUser called with:', user);
    if (!user || typeof user !== 'object') {
      console.warn('[STORAGE DEBUG] setUser - invalid user, skipping');
      return;
    }

    try {
      // Create a sanitized copy removing any potentially dangerous properties
      const sanitized = this.sanitizeUserObject(user);
      console.log('[STORAGE DEBUG] Storing user with key:', environment.userStorageKey, 'sanitized:', sanitized);
      this.persistentStorage.setItem(environment.userStorageKey, JSON.stringify(sanitized));
      console.log('[STORAGE DEBUG] User stored, localStorage contents:', Object.keys(localStorage));
    } catch (error) {
      console.error('[STORAGE DEBUG] Failed to store user:', error);
    }
  }

  /**
   * Sanitize user object to prevent stored XSS
   */
  private sanitizeUserObject<T>(obj: T): T {
    if (typeof obj !== 'object' || obj === null) {
      return obj;
    }

    const sanitized: Record<string, unknown> = {};

    for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {
      // Skip potentially dangerous keys
      if (key.startsWith('__') || key === 'constructor' || key === 'prototype') {
        continue;
      }

      if (typeof value === 'string') {
        // Basic HTML escape for string values
        sanitized[key] = this.escapeHtml(value);
      } else if (typeof value === 'object' && value !== null) {
        sanitized[key] = this.sanitizeUserObject(value);
      } else {
        sanitized[key] = value;
      }
    }

    return sanitized as T;
  }

  /**
   * Escape HTML special characters
   */
  private escapeHtml(str: string): string {
    const htmlEntities: Record<string, string> = {
      '&': '&amp;',
      '<': '&lt;',
      '>': '&gt;',
      '"': '&quot;',
      "'": '&#x27;',
    };
    return str.replace(/[&<>"']/g, (char) => htmlEntities[char] || char);
  }

  removeUser(): void {
    this.persistentStorage.removeItem(environment.userStorageKey);
  }

  clear(): void {
    this.removeToken();
    this.removeRefreshToken();
    this.removeUser();
  }

  /**
   * Get with type safety and validation
   */
  get<T>(key: string): T | null {
    if (!key || typeof key !== 'string') {
      return null;
    }

    const item = this.persistentStorage.getItem(key);
    if (item) {
      try {
        return JSON.parse(item) as T;
      } catch {
        return item as unknown as T;
      }
    }
    return null;
  }

  /**
   * Set with validation
   */
  set(key: string, value: unknown): void {
    if (!key || typeof key !== 'string') {
      return;
    }

    if (typeof value === 'string') {
      this.persistentStorage.setItem(key, value);
    } else {
      this.persistentStorage.setItem(key, JSON.stringify(value));
    }
  }

  remove(key: string): void {
    if (!key || typeof key !== 'string') {
      return;
    }
    this.persistentStorage.removeItem(key);
  }

  /**
   * Check if storage is available
   */
  isStorageAvailable(): boolean {
    try {
      const test = '__storage_test__';
      this.tokenStorage.setItem(test, test);
      this.tokenStorage.removeItem(test);
      return true;
    } catch {
      return false;
    }
  }
}
