// File: monorepo/web/maplefile-frontend/src/services/PasswordStorageService.js
// Enhanced with better activity detection and API integration

class PasswordStorageService {
  constructor() {
    this.password = null;
    this.sessionKey = null;
    this.timeout = null;
    this.inactivityTimeout = 7 * 24 * 60 * 60 * 1000; // 7 days (168 hours) - matches JWT refresh token lifetime
    this.isInitialized = false;
    this.initializationPromise = null; // Track initialization promise
    this.activityDetected = false;
    this.lastResetTime = 0; // Track when we last reset to throttle resets

    this.PREFERENCE_KEY = "mapleopentech_storage_preference";
    this.STORAGE_KEY = "mapleopentech_dev_pwd";
    this.STORAGE_METADATA_KEY = "mapleopentech_dev_pwd_meta";

    this.isDevelopment =
      import.meta.env.VITE_DEV_MODE === "true" || import.meta.env.DEV;

    // Determine storage mode from user preference (stored in localStorage)
    // Default to localStorage (true) for better UX if no preference is set
    const preference = this.getStoragePreference();
    const useLocalStorage = preference !== null ? preference : true; // Default to true
    this.storageMode = useLocalStorage ? "localStorage" : "sessionStorage";

    this.storage = this.getStorage();

    this.setupActivityListeners();
    this.setupApiActivityTracking();
    console.log(
      `[PasswordStorageService] Initialized with ${this.storageMode} mode`,
    );

    // Start initialization immediately (but don't block constructor)
    this.initializationPromise = this.initialize();
  }

  // Set user's storage preference (called from login page)
  setStoragePreference(useLocalStorage) {
    if (typeof window === "undefined") return;

    try {
      window.localStorage.setItem(
        this.PREFERENCE_KEY,
        JSON.stringify(useLocalStorage),
      );
      this.storageMode = useLocalStorage ? "localStorage" : "sessionStorage";
      this.storage = this.getStorage();
      console.log(
        `[PasswordStorageService] Storage preference set to ${this.storageMode}`,
      );
    } catch (error) {
      console.warn(
        "[PasswordStorageService] Failed to save storage preference:",
        error,
      );
    }
  }

  // Get user's storage preference
  // Returns: null (no preference set), true (localStorage), or false (sessionStorage)
  getStoragePreference() {
    if (typeof window === "undefined") return null;

    try {
      const preference = window.localStorage.getItem(this.PREFERENCE_KEY);
      // Return null if no preference has been set
      if (preference === null) return null;
      // Otherwise return the parsed boolean value
      return JSON.parse(preference) === true;
    } catch (error) {
      console.warn(
        "[PasswordStorageService] Failed to read storage preference:",
        error,
      );
      return null; // Return null on error to use default
    }
  }

  // Get the appropriate storage based on mode
  getStorage() {
    if (typeof window === "undefined") return null;

    if (this.storageMode === "localStorage") {
      return window.localStorage;
    }
    return window.sessionStorage;
  }

  // Store password temporarily
  setPassword(password) {
    this.password = password;
    this.storeEncryptedPassword();
    this.resetTimeout();
    console.log(
      `[PasswordStorageService] Password stored in ${this.storageMode}`,
    );
  }

  // Get stored password
  getPassword() {
    // First check memory
    if (this.password) {
      this.resetTimeout();
      return this.password;
    }

    // Try to restore from storage (works for both localStorage and sessionStorage)
    // For sessionStorage: password persists during page reloads within the same tab
    // For localStorage: password persists across browser sessions
    this.restorePasswordFromStorage();
    return this.password;
  }

  // Check if password is available (synchronous check)
  // Note: Password restoration from localStorage happens during initialize()
  // This method only checks if password is already in memory
  hasPassword() {
    return this.password !== null;
  }

  // Clear password from memory and storage
  clearPassword() {
    const hadPassword = this.password !== null;
    this.password = null;
    this.sessionKey = null;

    if (this.storage) {
      this.storage.removeItem(this.STORAGE_KEY);
      this.storage.removeItem(this.STORAGE_METADATA_KEY);
      this.storage.removeItem("mapleopentech_dev_session_key");
    }

    if (this.timeout) {
      clearTimeout(this.timeout);
      this.timeout = null;
    }

    if (hadPassword) {
      console.warn(
        "[PasswordStorageService] Password cleared - user will need to re-authenticate",
      );
    } else {
      console.log("[PasswordStorageService] Password cleared");
    }
  }

  // ENHANCED: Record API activity to extend password timeout
  recordApiActivity() {
    if (this.hasPassword()) {
      this.resetTimeout();
      // Only log occasionally in dev mode to avoid spam
      if (import.meta.env.DEV && Math.random() < 0.005) {
        // 0.5% chance
        console.log(
          "[PasswordStorageService] API activity detected - extending password timeout",
        );
      }
    }
  }

  // ENHANCED: Record successful token refresh
  recordTokenRefresh() {
    if (this.hasPassword()) {
      console.log(
        "[PasswordStorageService] Token refresh successful - extending password timeout",
      );
      this.resetTimeout();
    }
  }

  // ENHANCED: Record successful authentication operations
  recordAuthSuccess() {
    if (this.hasPassword()) {
      console.log(
        "[PasswordStorageService] Authentication success - extending password timeout",
      );
      this.resetTimeout();
    }
  }

  // Store encrypted password in storage
  async storeEncryptedPassword() {
    if (!this.password || !this.storage) return;

    try {
      // Generate session key for encryption
      this.sessionKey = window.crypto.getRandomValues(new Uint8Array(32));

      // Encrypt password
      const iv = window.crypto.getRandomValues(new Uint8Array(12));
      const key = await window.crypto.subtle.importKey(
        "raw",
        this.sessionKey,
        { name: "AES-GCM" },
        false,
        ["encrypt"],
      );

      const encrypted = await window.crypto.subtle.encrypt(
        { name: "AES-GCM", iv },
        key,
        new TextEncoder().encode(this.password),
      );

      // Store encrypted data
      const encryptedData = {
        data: Array.from(new Uint8Array(encrypted)),
        iv: Array.from(iv),
        timestamp: Date.now(),
      };

      this.storage.setItem(this.STORAGE_KEY, JSON.stringify(encryptedData));

      // Store metadata separately for easier checking
      this.storage.setItem(
        this.STORAGE_METADATA_KEY,
        JSON.stringify({
          timestamp: Date.now(),
          mode: this.storageMode,
          isDev: this.isDevelopment,
        }),
      );

      // Store session key in storage (needed for restore after refresh)
      // For localStorage: persists across browser sessions
      // For sessionStorage: persists during page reloads within the same tab
      this.storage.setItem(
        "mapleopentech_dev_session_key",
        JSON.stringify(Array.from(this.sessionKey)),
      );
    } catch (error) {
      console.warn(
        "[PasswordStorageService] Failed to store encrypted password:",
        error,
      );
    }
  }

  // Restore password from storage (enhanced for dev mode)
  async restorePasswordFromStorage() {
    if (!this.storage) return false;

    const stored = this.storage.getItem(this.STORAGE_KEY);
    const _metadata = this.storage.getItem(this.STORAGE_METADATA_KEY); // Reserved for future use

    if (!stored) return false;

    try {
      const { data, iv, timestamp } = JSON.parse(stored);

      // Check if expired
      if (Date.now() - timestamp > this.inactivityTimeout) {
        console.log(
          "[PasswordStorageService] Stored password expired, clearing",
        );
        this.clearPassword();
        return false;
      }

      // Get session key from storage if not in memory
      // Works for both localStorage and sessionStorage modes
      if (!this.sessionKey) {
        const storedKey = this.storage.getItem("mapleopentech_dev_session_key");
        if (storedKey) {
          this.sessionKey = new Uint8Array(JSON.parse(storedKey));
        }
      }

      if (!this.sessionKey) {
        console.warn(
          "[PasswordStorageService] No session key available for decryption",
        );
        this.clearPassword();
        return false;
      }

      // Decrypt password
      const key = await window.crypto.subtle.importKey(
        "raw",
        this.sessionKey,
        { name: "AES-GCM" },
        false,
        ["decrypt"],
      );

      const decrypted = await window.crypto.subtle.decrypt(
        { name: "AES-GCM", iv: new Uint8Array(iv) },
        key,
        new Uint8Array(data),
      );

      this.password = new TextDecoder().decode(decrypted);
      this.resetTimeout();

      console.log("[PasswordStorageService] Password restored from storage");
      return true;
    } catch (error) {
      console.warn(
        "[PasswordStorageService] Failed to restore password:",
        error,
      );
      this.clearPassword();
      return false;
    }
  }

  // Original restorePassword method for backward compatibility
  async restorePassword() {
    return this.restorePasswordFromStorage();
  }

  // ENHANCED: Reset inactivity timeout with throttling
  resetTimeout() {
    const now = Date.now();
    const timeSinceLastReset = now - this.lastResetTime;
    const THROTTLE_MS = 1000; // Only reset once per second

    // Throttle: Skip reset if we reset less than 1 second ago
    if (timeSinceLastReset < THROTTLE_MS && this.timeout) {
      // Skip this reset to avoid excessive resets
      if (import.meta.env.DEV && Math.random() < 0.001) {
        // 0.1% chance to log throttled resets
        console.log(
          `[PasswordStorageService] ⏭️ Reset throttled (${timeSinceLastReset}ms since last reset)`,
        );
      }
      return;
    }

    if (this.timeout) {
      clearTimeout(this.timeout);
    }

    const timeoutMinutes = Math.floor(this.inactivityTimeout / 60000);

    // Track when timeout was last reset for debugging
    this.lastTimeoutReset = now;
    this.lastResetTime = now; // Update throttle tracker
    this.timeoutResetCount = (this.timeoutResetCount || 0) + 1;

    this.timeout = setTimeout(() => {
      console.warn(
        `[PasswordStorageService] Password expired after ${timeoutMinutes} minutes of inactivity`,
      );
      console.warn(
        `[PasswordStorageService] Timeout was reset ${this.timeoutResetCount} times before expiring`,
      );
      this.clearPassword();

      // Dispatch a custom event to notify the app
      if (typeof window !== "undefined") {
        window.dispatchEvent(
          new CustomEvent("passwordExpired", {
            detail: { reason: "inactivity_timeout" },
          }),
        );
      }
    }, this.inactivityTimeout);

    // Minimal logging - only log every 100th reset
    if (import.meta.env.DEV && this.timeoutResetCount % 100 === 0) {
      const now = new Date();
      const expiryTime = new Date(now.getTime() + this.inactivityTimeout);
      console.log(
        `[PasswordStorageService] Timeout reset #${this.timeoutResetCount} - expires at ${expiryTime.toLocaleTimeString()}`,
      );
    }
  }

  // ENHANCED: Setup comprehensive activity listeners
  setupActivityListeners() {
    if (typeof document === "undefined") return;

    // Comprehensive list of user activity events
    const events = [
      "click",
      "keydown",
      "keyup",
      "keypress",
      "mousemove",
      "mousedown",
      "mouseup",
      "scroll",
      "wheel",
      "touchstart",
      "touchmove",
      "touchend",
      "focus",
      "blur",
      "visibilitychange",
    ];

    // Track activity event counts for debugging
    this.activityEventCounts = this.activityEventCounts || {};

    const activityHandler = (event) => {
      if (this.hasPassword()) {
        this.activityDetected = true;

        // Minimal activity logging - only count, don't log
        if (import.meta.env.DEV) {
          this.activityEventCounts[event.type] =
            (this.activityEventCounts[event.type] || 0) + 1;
        }

        this.resetTimeout();
      } else {
        // Debug: Log when activity is detected but no password
        if (import.meta.env.DEV && Math.random() < 0.01) {
          console.log(
            `[PasswordStorageService] ⚠️ Activity detected (${event.type}) but no password available`,
          );
        }
      }
    };

    events.forEach((event) => {
      document.addEventListener(event, activityHandler, { passive: true });
    });

    // Special handling for visibility change
    document.addEventListener(
      "visibilitychange",
      () => {
        if (!document.hidden && this.hasPassword()) {
          // Only log when page becomes visible, not on every activity
          if (import.meta.env.DEV) {
            console.log(
              "[PasswordStorageService] Page became visible - extending password timeout",
            );
          }
          this.resetTimeout();
        }
      },
      { passive: true },
    );

    console.log(
      "[PasswordStorageService] Enhanced activity listeners setup complete",
    );
  }

  // ENHANCED: Setup API activity tracking
  setupApiActivityTracking() {
    if (typeof window === "undefined") return;

    // Override fetch to track API activity
    const originalFetch = window.fetch;
    window.fetch = async (...args) => {
      try {
        const response = await originalFetch(...args);

        // If the request was successful and we have a password, extend timeout
        if (response.ok && this.hasPassword()) {
          this.recordApiActivity();
        }

        return response;
      } catch (error) {
        // Even failed requests count as activity
        if (this.hasPassword()) {
          this.recordApiActivity();
        }
        throw error;
      }
    };

    console.log(
      "[PasswordStorageService] API activity tracking setup complete",
    );
  }

  // Initialize the service
  async initialize() {
    // Prevent double initialization
    if (this.isInitialized) {
      console.log("[PasswordStorageService] Already initialized, skipping");
      return;
    }

    // Mark as initialized immediately to prevent concurrent calls
    this.isInitialized = true;

    try {
      // Try to restore password from storage (works for both modes)
      // For localStorage: persists across browser sessions
      // For sessionStorage: persists during page reloads within the same tab
      console.log(
        `[PasswordStorageService] Attempting to restore password from ${this.storageMode}...`,
      );
      const restored = await this.restorePasswordFromStorage();
      if (restored) {
        console.log(
          `[PasswordStorageService] ✅ Password successfully restored from ${this.storageMode}`,
        );
      } else {
        console.log(
          `[PasswordStorageService] ℹ️ No valid password found in ${this.storageMode} (user may not be logged in yet)`,
        );
      }

      console.log(
        "[PasswordStorageService] ✅ Service initialized successfully",
      );
    } catch (error) {
      console.error(
        "[PasswordStorageService] ❌ Initialization failed:",
        error,
      );
      // Keep isInitialized = true to prevent repeated failed attempts
    }
  }

  // Get storage mode info (useful for debugging)
  getStorageInfo() {
    return {
      mode: this.storageMode,
      isDevelopment: this.isDevelopment,
      hasPassword: this.hasPassword(),
      isInitialized: this.isInitialized,
      storageAvailable: !!this.storage,
      timeoutMinutes: Math.floor(this.inactivityTimeout / 60000),
      activityDetected: this.activityDetected,
    };
  }

  // ENHANCED: Set custom inactivity timeout with validation
  setInactivityTimeout(minutes) {
    if (minutes < 5) {
      console.warn("[PasswordStorageService] Minimum timeout is 5 minutes");
      minutes = 5;
    }
    if (minutes > 480) {
      // 8 hours max
      console.warn(
        "[PasswordStorageService] Maximum timeout is 8 hours (480 minutes)",
      );
      minutes = 480;
    }

    this.inactivityTimeout = minutes * 60 * 1000;
    this.resetTimeout();
    console.log(
      `[PasswordStorageService] Inactivity timeout set to ${minutes} minutes`,
    );
  }

  // Force clear all stored data (useful for debugging)
  forceClearAll() {
    this.clearPassword();

    if (this.storage) {
      // Clear dev session key if exists
      this.storage.removeItem("mapleopentech_dev_session_key");
    }

    console.log("[PasswordStorageService] All data force cleared");
  }

  // ENHANCED: Get detailed status for debugging
  getDetailedStatus() {
    const hasPassword = this.hasPassword();
    const now = Date.now();
    const timeRemaining = this.lastTimeoutReset
      ? Math.floor(
          (this.lastTimeoutReset + this.inactivityTimeout - now) / 60000,
        )
      : 0;
    const timeRemainingSeconds = this.lastTimeoutReset
      ? Math.floor(
          (this.lastTimeoutReset + this.inactivityTimeout - now) / 1000,
        )
      : 0;

    return {
      hasPassword,
      timeoutActive: !!this.timeout,
      timeRemainingMinutes: Math.max(0, timeRemaining),
      timeRemainingSeconds: Math.max(0, timeRemainingSeconds),
      timeRemainingFormatted: this.formatTimeRemaining(timeRemainingSeconds),
      lastTimeoutReset: this.lastTimeoutReset
        ? new Date(this.lastTimeoutReset).toLocaleTimeString()
        : "Never",
      expiryTime: this.lastTimeoutReset
        ? new Date(
            this.lastTimeoutReset + this.inactivityTimeout,
          ).toLocaleTimeString()
        : "N/A",
      resetCount: this.timeoutResetCount || 0,
      activityEventCounts: this.activityEventCounts || {},
      totalActivityEvents: Object.values(this.activityEventCounts || {}).reduce(
        (sum, count) => sum + count,
        0,
      ),
      storageInfo: this.getStorageInfo(),
    };
  }

  // Format time remaining as HH:MM:SS
  formatTimeRemaining(seconds) {
    if (seconds <= 0) return "Expired";
    const hours = Math.floor(seconds / 3600);
    const minutes = Math.floor((seconds % 3600) / 60);
    const secs = seconds % 60;
    return `${hours}:${String(minutes).padStart(2, "0")}:${String(secs).padStart(2, "0")}`;
  }
}

// Create singleton instance (initialization happens in constructor)
const passwordStorageService = new PasswordStorageService();

// In development, add to window for debugging
if (import.meta.env.DEV) {
  window.__passwordService = passwordStorageService;

  // Add convenient debug helpers
  window.__passwordDebug = {
    // Get current status
    status: () => {
      const status = passwordStorageService.getDetailedStatus();
      console.log("=== Password Service Status ===");
      console.log("Has Password:", status.hasPassword);
      console.log("Timeout Active:", status.timeoutActive);
      console.log("Time Remaining:", status.timeRemainingFormatted);
      console.log("Last Reset:", status.lastTimeoutReset);
      console.log("Expires At:", status.expiryTime);
      console.log("Reset Count:", status.resetCount);
      console.log("Total Activity Events:", status.totalActivityEvents);
      console.log("\nActivity Breakdown:");
      Object.entries(status.activityEventCounts).forEach(([event, count]) => {
        console.log(`  ${event}: ${count}`);
      });
      console.log("\nStorage Info:", status.storageInfo);
      return status;
    },

    // Watch timeout resets in real-time
    watch: () => {
      console.log("👁️ Watching password timeout resets (check console)...");
      console.log("Try moving your mouse, clicking, or typing to see resets");
      console.log("Call window.__passwordDebug.stopWatch() to stop");

      window.__passwordDebugInterval = setInterval(() => {
        const status = passwordStorageService.getDetailedStatus();
        const secondsSinceReset = Math.floor(
          (Date.now() - passwordStorageService.lastResetTime) / 1000,
        );
        console.log(
          `⏰ Time remaining: ${status.timeRemainingFormatted} | Resets: ${status.resetCount} | Events: ${status.totalActivityEvents} | Last reset: ${secondsSinceReset}s ago`,
        );
      }, 5000);
    },

    // Stop watching
    stopWatch: () => {
      if (window.__passwordDebugInterval) {
        clearInterval(window.__passwordDebugInterval);
        window.__passwordDebugInterval = null;
        console.log("✅ Stopped watching password timeout");
      }
    },

    // Manually trigger timeout reset
    reset: () => {
      if (passwordStorageService.hasPassword()) {
        passwordStorageService.resetTimeout();
        console.log("✅ Timeout manually reset");
      } else {
        console.warn("⚠️ No password available to reset timeout");
      }
    },

    // Get activity event counts
    events: () => {
      const counts = passwordStorageService.activityEventCounts || {};
      console.log("=== Activity Event Counts ===");
      Object.entries(counts)
        .sort((a, b) => b[1] - a[1])
        .forEach(([event, count]) => {
          console.log(`${event}: ${count}`);
        });
      return counts;
    },
  };

  console.log("[PasswordStorageService] Debug access available:");
  console.log("  window.__passwordService - Direct service access");
  console.log("  window.__passwordDebug.status() - Get current status");
  console.log("  window.__passwordDebug.watch() - Watch timeout in real-time");
  console.log("  window.__passwordDebug.stopWatch() - Stop watching");
  console.log("  window.__passwordDebug.reset() - Manually reset timeout");
  console.log("  window.__passwordDebug.events() - Show activity event counts");
}

export default passwordStorageService;
