/**
 * Design System Server (DSS) - App Store
 *
 * Centralized state management with reactive subscriptions
 * for managing application state across components and pages.
 */

class AppStore {
  constructor() {
    this.state = {
      // User & Auth
      user: null,
      team: null,
      role: null,

      // Navigation
      currentPage: 'dashboard',
      currentProject: null,
      sidebarOpen: true,

      // Data
      projects: [],
      tokens: [],
      components: [],
      styles: [],

      // Discovery
      discovery: null,
      health: null,
      activity: [],
      stats: null,

      // Figma
      figmaConnected: false,
      figmaFileKey: null,
      lastSync: null,

      // Configuration (loaded from server /api/config)
      serverConfig: null,
      isConfigLoading: true,
      configError: null,

      // UI State
      loading: {},
      errors: {},
      notifications: []
    };

    this.listeners = new Map();
    this.middleware = [];

    // Track in-flight requests to prevent duplicates
    this.pendingRequests = new Map();
  }

  // === State Access ===

  get(key) {
    return key ? this.state[key] : this.state;
  }

  // === State Updates ===

  set(updates, silent = false) {
    const prevState = { ...this.state };

    // Apply middleware
    for (const mw of this.middleware) {
      updates = mw(updates, prevState);
    }

    this.state = { ...this.state, ...updates };

    if (!silent) {
      this._notify(updates, prevState);
    }
  }

  // === Subscriptions ===

  subscribe(key, callback) {
    if (!this.listeners.has(key)) {
      this.listeners.set(key, new Set());
    }
    this.listeners.get(key).add(callback);

    // Return unsubscribe function
    return () => {
      this.listeners.get(key)?.delete(callback);
    };
  }

  subscribeAll(callback) {
    return this.subscribe('*', callback);
  }

  _notify(updates, prevState) {
    // Notify specific key listeners
    for (const key of Object.keys(updates)) {
      const listeners = this.listeners.get(key);
      if (listeners) {
        listeners.forEach(cb => cb(updates[key], prevState[key], key));
      }
    }

    // Notify global listeners
    const globalListeners = this.listeners.get('*');
    if (globalListeners) {
      globalListeners.forEach(cb => cb(this.state, prevState));
    }
  }

  // === Middleware ===

  use(middleware) {
    this.middleware.push(middleware);
  }

  // === Loading State ===

  setLoading(key, loading = true) {
    this.set({
      loading: { ...this.state.loading, [key]: loading }
    });
  }

  isLoading(key) {
    return this.state.loading[key] || false;
  }

  // === Error State ===

  setError(key, error) {
    this.set({
      errors: { ...this.state.errors, [key]: error }
    });
  }

  clearError(key) {
    const errors = { ...this.state.errors };
    delete errors[key];
    this.set({ errors });
  }

  // === Notifications ===

  notify(message, type = 'info', duration = 5000) {
    const notification = {
      id: Date.now(),
      message,
      type,
      timestamp: new Date()
    };

    this.set({
      notifications: [...this.state.notifications, notification]
    });

    if (duration > 0) {
      setTimeout(() => this.dismissNotification(notification.id), duration);
    }

    return notification.id;
  }

  dismissNotification(id) {
    this.set({
      notifications: this.state.notifications.filter(n => n.id !== id)
    });
  }

  // === User & Auth ===

  setUser(user, team = null, role = null) {
    this.set({ user, team, role });
  }

  logout() {
    this.set({
      user: null,
      team: null,
      role: null,
      projects: [],
      tokens: [],
      components: [],
      currentProject: null
    });
    localStorage.removeItem('currentProject');
  }

  hasPermission(permission) {
    const rolePermissions = {
      SUPER_ADMIN: ['*'],
      TEAM_LEAD: ['read', 'write', 'sync', 'manage_team'],
      DEVELOPER: ['read', 'write', 'sync'],
      VIEWER: ['read']
    };

    const perms = rolePermissions[this.state.role] || [];
    return perms.includes('*') || perms.includes(permission);
  }

  // === Projects ===

  /**
   * Fetch all projects from API
   */
  async fetchProjects() {
    this.setLoading('projects', true);
    try {
      const response = await fetch('/api/projects');

      if (!response.ok) throw new Error(`API Error: ${response.status}`);

      const json = await response.json();

      if (json.status === 'success') {
        // Clear any previous errors on success
        this.setError('projects', null);
        this.setProjects(json.data.projects || []);

        // Auto-restore last selected project if available
        if (!this.state.currentProject && json.data.projects.length > 0) {
          const stored = localStorage.getItem('currentProject');
          if (stored) {
            try {
              const parsed = JSON.parse(stored);
              const exists = json.data.projects.find(p => p.id === parsed.id);
              if (exists) this.setProject(exists);
            } catch (e) {
              console.warn('Invalid stored project', e);
            }
          }
        }
      } else {
        throw new Error(json.message || 'Failed to fetch projects');
      }
    } catch (error) {
      console.error('Project fetch error:', error);
      this.setError('projects', error.message);
    } finally {
      this.setLoading('projects', false);
    }
  }

  setProjects(projects) {
    this.set({ projects });
  }

  setProject(project) {
    this.set({ currentProject: project });
    if (project) {
      localStorage.setItem('currentProject', JSON.stringify(project));
    } else {
      localStorage.removeItem('currentProject');
    }
  }

  async getProjectConfig() {
    if (!this.state.currentProject) return null;

    this.setLoading('config', true);
    try {
      const response = await fetch(`/api/config/${this.state.currentProject.id}/resolved`);

      if (!response.ok) throw new Error(`API Error: ${response.status}`);

      const json = await response.json();

      if (json.status === 'success') {
        // Clear any previous errors on success
        this.setError('config', null);
        return json.data.config;
      } else {
        throw new Error(json.message || 'Failed to fetch config');
      }
    } catch (error) {
      console.error('Config fetch error:', error);
      // Set error state so UI can display retry option
      this.setError('config', error.message);
      return null;
    } finally {
      this.setLoading('config', false);
    }
  }

  /**
   * Fetch all components from registry
   * Includes request deduplication to prevent duplicate API calls
   */
  async fetchComponents() {
    const requestKey = 'components';

    // Return existing promise if request is in flight
    if (this.pendingRequests.has(requestKey)) {
      return this.pendingRequests.get(requestKey);
    }

    // Create new request promise
    const requestPromise = (async () => {
      this.setLoading('components', true);
      try {
        const response = await fetch('/api/components');

        if (!response.ok) throw new Error(`API Error: ${response.status}`);

        const json = await response.json();

        if (json.status === 'success') {
          this.setError('components', null);
          this.setComponents(json.data.components || []);
        } else {
          throw new Error(json.message || 'Failed to fetch components');
        }
      } catch (error) {
        console.error('Components fetch error:', error);
        this.setError('components', error.message);
        throw error;
      } finally {
        this.setLoading('components', false);
        this.pendingRequests.delete(requestKey);
      }
    })();

    this.pendingRequests.set(requestKey, requestPromise);
    return requestPromise;
  }

  /**
   * Fetch design tokens for a project
   * Includes request deduplication to prevent duplicate API calls
   */
  async fetchTokens(projectId) {
    if (!projectId) {
      console.warn('fetchTokens called without projectId');
      return;
    }

    const requestKey = `tokens:${projectId}`;

    // Return existing promise if request is in flight
    if (this.pendingRequests.has(requestKey)) {
      return this.pendingRequests.get(requestKey);
    }

    // Create new request promise
    const requestPromise = (async () => {
      this.setLoading('tokens', true);
      try {
        const response = await fetch(`/api/projects/${projectId}/tokens`);

        if (!response.ok) throw new Error(`API Error: ${response.status}`);

        const json = await response.json();

        if (json.status === 'success') {
          this.setError('tokens', null);
          this.setTokens(json.data.tokens || []);
        } else {
          throw new Error(json.message || 'Failed to fetch tokens');
        }
      } catch (error) {
        console.error('Tokens fetch error:', error);
        this.setError('tokens', error.message);
        throw error;
      } finally {
        this.setLoading('tokens', false);
        this.pendingRequests.delete(requestKey);
      }
    })();

    this.pendingRequests.set(requestKey, requestPromise);
    return requestPromise;
  }

  /**
   * Fetch discovery scan results for a project
   * Includes request deduplication to prevent duplicate API calls
   */
  async fetchDiscoveryResults(projectId) {
    if (!projectId) {
      console.warn('fetchDiscoveryResults called without projectId');
      return;
    }

    const requestKey = `discovery:${projectId}`;

    // Return existing promise if request is in flight
    if (this.pendingRequests.has(requestKey)) {
      return this.pendingRequests.get(requestKey);
    }

    // Create new request promise
    const requestPromise = (async () => {
      this.setLoading('discovery', true);
      try {
        const response = await fetch(`/api/discovery/scan?projectId=${projectId}`);

        if (!response.ok) throw new Error(`API Error: ${response.status}`);

        const json = await response.json();

        if (json.status === 'success') {
          this.setError('discovery', null);
          this.setDiscovery(json.data || null);
        } else {
          throw new Error(json.message || 'Failed to fetch discovery results');
        }
      } catch (error) {
        console.error('Discovery fetch error:', error);
        this.setError('discovery', error.message);
        throw error;
      } finally {
        this.setLoading('discovery', false);
        this.pendingRequests.delete(requestKey);
      }
    })();

    this.pendingRequests.set(requestKey, requestPromise);
    return requestPromise;
  }

  addProject(project) {
    this.set({
      projects: [...this.state.projects, project]
    });
  }

  updateProject(id, updates) {
    this.set({
      projects: this.state.projects.map(p =>
        p.id === id ? { ...p, ...updates } : p
      )
    });
  }

  // === Figma ===

  setFigmaConnected(connected, fileKey = null) {
    this.set({
      figmaConnected: connected,
      figmaFileKey: fileKey
    });
  }

  setLastSync(timestamp) {
    this.set({ lastSync: timestamp });
  }

  // === Tokens ===

  setTokens(tokens) {
    this.set({ tokens });
  }

  getTokensByCategory(category) {
    return this.state.tokens.filter(t => t.category === category);
  }

  // === Components ===

  setComponents(components) {
    this.set({ components });
  }

  // === Discovery ===

  setDiscovery(discovery) {
    this.set({ discovery });
  }

  setHealth(health) {
    this.set({ health });
  }

  setActivity(activity) {
    this.set({ activity });
  }

  setStats(stats) {
    this.set({ stats });
  }

  // === Persistence ===

  persist() {
    const toPersist = {
      user: this.state.user,
      team: this.state.team,
      role: this.state.role,
      figmaFileKey: this.state.figmaFileKey,
      sidebarOpen: this.state.sidebarOpen
      // NOTE: serverConfig is loaded from /api/config, not persisted locally
    };
    localStorage.setItem('dss-store', JSON.stringify(toPersist));
  }

  hydrate() {
    try {
      // Hydrate general store data
      const stored = localStorage.getItem('dss-store');
      if (stored) {
        const data = JSON.parse(stored);
        this.set(data, true);
      }

      // Hydrate project context separately
      const storedProject = localStorage.getItem('currentProject');
      if (storedProject) {
        const project = JSON.parse(storedProject);
        this.set({ currentProject: project }, true);
      }
    } catch (e) {
      console.warn('Failed to hydrate store:', e);
    }
  }

  // === Debug ===

  debug() {
    console.group('App Store State');
    console.log('State:', this.state);
    console.log('Listeners:', Array.from(this.listeners.keys()));
    console.groupEnd();
  }
}

// Logging middleware
const loggingMiddleware = (updates, prevState) => {
  if (window.DEBUG) {
    console.log('[Store Update]', updates);
  }
  return updates;
};

// Create and export singleton
const store = new AppStore();
store.use(loggingMiddleware);
store.hydrate();

// Auto-persist on important changes
store.subscribe('user', () => store.persist());
store.subscribe('team', () => store.persist());
store.subscribe('figmaFileKey', () => store.persist());
// NOTE: serverConfig is NOT persisted - loaded fresh from /api/config on each init

export { AppStore };
export default store;
