/**
 * api-client.js
 * Unified API client for backend communication
 * Handles authentication, request/response formatting, error handling
 * Supports incognito mode with sessionStorage fallback (lazy-initialized)
 */

class ApiClient {
  constructor(baseURL = null) {
    // Use relative path /api which works for both production (proxied through nginx)
    // and development (proxied through Vite to backend on localhost:8002)
    this.baseURL = baseURL || '/api';
    this.accessToken = null;
    this.refreshToken = null;
    this.loadTokensFromStorage();
    this.setupAuthChangeListener();
  }

  setupAuthChangeListener() {
    // Listen for auth changes (e.g., from demo-auth-init.js or other components)
    // and reload tokens from storage
    document.addEventListener('auth-change', () => {
      console.log('[ApiClient] Auth change detected, reloading tokens...');
      this.loadTokensFromStorage();
    });
    window.addEventListener('auth-change', () => {
      console.log('[ApiClient] Auth change detected, reloading tokens...');
      this.loadTokensFromStorage();
    });
  }

  loadTokensFromStorage() {
    // Always use localStorage directly for auth tokens
    // This ensures compatibility with demo-auth-init.js which also uses localStorage
    const stored = localStorage.getItem('auth_tokens');
    if (stored) {
      try {
        const parsed = JSON.parse(stored);
        console.log('[ApiClient] Parsed tokens object:', { hasAccessToken: !!parsed.accessToken, hasRefreshToken: !!parsed.refreshToken });
        this.accessToken = parsed.accessToken;
        this.refreshToken = parsed.refreshToken;
        console.log('[ApiClient] Tokens assigned - accessToken:', this.accessToken ? `${this.accessToken.substring(0, 20)}...` : 'null');
      } catch (e) {
        console.error('[ApiClient] Failed to parse stored tokens:', e, 'stored value:', stored);
      }
    } else {
      console.warn('[ApiClient] No auth_tokens found in localStorage');
    }
  }

  saveTokensToStorage() {
    try {
      // Always use localStorage directly for auth tokens
      localStorage.setItem('auth_tokens', JSON.stringify({
        accessToken: this.accessToken,
        refreshToken: this.refreshToken
      }));
    } catch (e) {
      console.warn('[ApiClient] Failed to save tokens to storage:', e);
    }
  }

  clearTokens() {
    this.accessToken = null;
    this.refreshToken = null;
    localStorage.removeItem('auth_tokens');
  }

  async request(method, endpoint, data = null) {
    // Always reload tokens from storage before making a request
    // This handles the case where tokens were set after API client initialization (e.g., auto-login)
    this.loadTokensFromStorage();

    const url = `${this.baseURL}${endpoint}`;
    const options = {
      method,
      headers: {
        'Content-Type': 'application/json'
      }
    };

    if (this.accessToken) {
      options.headers['Authorization'] = `Bearer ${this.accessToken}`;
      console.log(`[ApiClient] ${method} ${endpoint}: Token present (${this.accessToken.substring(0, 20)}...)`);
    } else {
      console.warn(`[ApiClient] ${method} ${endpoint}: No token provided`);
    }

    if (data) {
      options.body = JSON.stringify(data);
    }

    try {
      const response = await fetch(url, options);
      const result = await response.json();

      if (!response.ok) {
        // Handle token refresh
        if (response.status === 403 && result.code === 'FORBIDDEN' && this.refreshToken) {
          await this.refreshAccessToken();
          return this.request(method, endpoint, data);
        }

        throw new Error(result.message || 'API request failed');
      }

      // Handle both response formats:
      // 1. Standard wrapped response: { data: {...} }
      // 2. Direct array response: [...]
      // 3. Direct object response: {...}
      if (Array.isArray(result)) {
        return result;
      }
      return result.data || result;
    } catch (error) {
      console.error(`[ApiClient] ${method} ${endpoint}:`, error);
      throw error;
    }
  }

  async refreshAccessToken() {
    try {
      const response = await fetch(`${this.baseURL}/auth/refresh`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ refreshToken: this.refreshToken })
      });

      const result = await response.json();
      if (result.status === 'success') {
        this.accessToken = result.data.tokens.accessToken;
        this.refreshToken = result.data.tokens.refreshToken;
        this.saveTokensToStorage();
        this.dispatchAuthChangeEvent('token-refresh');
        return true;
      }

      this.clearTokens();
      this.dispatchAuthChangeEvent('token-expired');
      return false;
    } catch (error) {
      console.error('[ApiClient] Token refresh failed:', error);
      this.clearTokens();
      this.dispatchAuthChangeEvent('token-refresh-error');
      return false;
    }
  }

  /**
   * Dispatch auth-change event to notify all listeners of authentication state changes
   */
  dispatchAuthChangeEvent(reason = 'auth-change') {
    const event = new CustomEvent('auth-change', {
      detail: { reason, hasToken: !!this.accessToken },
      bubbles: true,
      composed: true
    });
    document.dispatchEvent(event);
    window.dispatchEvent(event);
  }

  // Auth endpoints
  async register(email, password, name) {
    const result = await this.request('POST', '/auth/register', {
      email, password, name
    });
    this.accessToken = result.tokens.accessToken;
    this.refreshToken = result.tokens.refreshToken;
    this.saveTokensToStorage();
    this.dispatchAuthChangeEvent('user-registered');
    return result.user;
  }

  async login(email, password) {
    const result = await this.request('POST', '/auth/login', {
      email, password
    });
    this.accessToken = result.tokens.accessToken;
    this.refreshToken = result.tokens.refreshToken;
    this.saveTokensToStorage();
    this.dispatchAuthChangeEvent('user-logged-in');
    return result.user;
  }

  async getMe() {
    const result = await this.request('GET', '/auth/me');
    return result.user;
  }

  async logout() {
    this.clearTokens();
    this.dispatchAuthChangeEvent('user-logged-out');
  }

  // Project endpoints
  async getProjects() {
    const result = await this.request('GET', '/projects');
    // Handle both response formats:
    // 1. Wrapped response: { data: { projects: [...] } }
    // 2. Direct array response: [...]
    if (Array.isArray(result)) {
      return result;
    }
    return result.projects || result.data?.projects || [];
  }

  async getProject(id) {
    const result = await this.request('GET', `/projects/${id}`);
    return result.project;
  }

  async createProject(data) {
    const result = await this.request('POST', '/projects', data);
    return result.project;
  }

  async updateProject(id, data) {
    const result = await this.request('PUT', `/projects/${id}`, data);
    return result.project;
  }

  async deleteProject(id) {
    await this.request('DELETE', `/projects/${id}`);
  }

  // Token endpoints
  async getTokens(projectId, category = null) {
    const url = `/tokens/project/${projectId}${category ? `?category=${category}` : ''}`;
    const result = await this.request('GET', url);
    return result.tokens;
  }

  async createToken(data) {
    const result = await this.request('POST', '/tokens', data);
    return result.token;
  }

  async updateToken(id, data) {
    const result = await this.request('PUT', `/tokens/${id}`, data);
    return result.token;
  }

  async deleteToken(id) {
    await this.request('DELETE', `/tokens/${id}`);
  }

  // Component endpoints
  async getComponents(projectId) {
    const result = await this.request('GET', `/components/project/${projectId}`);
    return result.components;
  }

  async createComponent(data) {
    const result = await this.request('POST', '/components', data);
    return result.component;
  }

  async updateComponent(id, data) {
    const result = await this.request('PUT', `/components/${id}`, data);
    return result.component;
  }

  async deleteComponent(id) {
    await this.request('DELETE', `/components/${id}`);
  }
}

export default new ApiClient();
