import Cookies from 'js-cookie';
import { atom, map } from 'nanostores';
import { createScopedLogger } from '~/utils/logger';

const logger = createScopedLogger('LogStore');

export interface LogEntry {
  id: string;
  timestamp: string;
  level: 'info' | 'warning' | 'error' | 'debug';
  message: string;
  details?: Record<string, any>;
  category:
    | 'system'
    | 'provider'
    | 'user'
    | 'error'
    | 'api'
    | 'auth'
    | 'database'
    | 'network'
    | 'performance'
    | 'settings'
    | 'task'
    | 'update'
    | 'feature';
  subCategory?: string;
  duration?: number;
  statusCode?: number;
  source?: string;
  stack?: string;
  metadata?: {
    component?: string;
    action?: string;
    userId?: string;
    sessionId?: string;
    previousValue?: any;
    newValue?: any;
  };
}

interface LogDetails extends Record<string, any> {
  type: string;
  message: string;
}

const MAX_LOGS = 1000; // Maximum number of logs to keep in memory

class LogStore {
  private _logs = map<Record<string, LogEntry>>({});
  showLogs = atom(true);
  private _readLogs = new Set<string>();

  constructor() {
    // Load saved logs from cookies on initialization
    this._loadLogs();

    // Only load read logs in browser environment
    if (typeof window !== 'undefined') {
      this._loadReadLogs();
    }
  }

  // Expose the logs store for subscription
  get logs() {
    return this._logs;
  }

  private _loadLogs() {
    const savedLogs = Cookies.get('eventLogs');

    if (savedLogs) {
      try {
        const parsedLogs = JSON.parse(savedLogs);
        this._logs.set(parsedLogs);
      } catch (error) {
        logger.error('Failed to parse logs from cookies:', error);
      }
    }
  }

  private _loadReadLogs() {
    if (typeof window === 'undefined') {
      return;
    }

    const savedReadLogs = localStorage.getItem('upage_read_logs');

    if (savedReadLogs) {
      try {
        const parsedReadLogs = JSON.parse(savedReadLogs);
        this._readLogs = new Set(parsedReadLogs);
      } catch (error) {
        logger.error('Failed to parse read logs:', error);
      }
    }
  }

  private _saveLogs() {
    const currentLogs = this._logs.get();
    Cookies.set('eventLogs', JSON.stringify(currentLogs));
  }

  private _saveReadLogs() {
    if (typeof window === 'undefined') {
      return;
    }

    localStorage.setItem('upage_read_logs', JSON.stringify(Array.from(this._readLogs)));
  }

  private _generateId(): string {
    return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
  }

  private _trimLogs() {
    const currentLogs = Object.entries(this._logs.get());

    if (currentLogs.length > MAX_LOGS) {
      const sortedLogs = currentLogs.sort(
        ([, a], [, b]) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(),
      );
      const newLogs = Object.fromEntries(sortedLogs.slice(0, MAX_LOGS));
      this._logs.set(newLogs);
    }
  }

  // Base log method for general logging
  private _addLog(
    message: string,
    level: LogEntry['level'],
    category: LogEntry['category'],
    details?: Record<string, any>,
    metadata?: LogEntry['metadata'],
  ) {
    const id = this._generateId();
    const entry: LogEntry = {
      id,
      timestamp: new Date().toISOString(),
      level,
      message,
      details,
      category,
      metadata,
    };

    this._logs.setKey(id, entry);
    this._trimLogs();
    this._saveLogs();

    return id;
  }

  // Specialized method for API logging
  private _addApiLog(
    message: string,
    method: string,
    _url: string,
    details: {
      method: string;
      url: string;
      statusCode: number;
      duration: number;
      request: any;
      response: any;
    },
  ) {
    const statusCode = details.statusCode;
    return this._addLog(message, statusCode >= 400 ? 'error' : 'info', 'api', details, {
      component: 'api',
      action: method,
    });
  }

  // System events
  logSystem(message: string, details?: Record<string, any>) {
    return this._addLog(message, 'info', 'system', details);
  }

  // Provider events
  logProvider(message: string, details?: Record<string, any>) {
    return this._addLog(message, 'info', 'provider', details);
  }

  // User actions
  logUserAction(message: string, details?: Record<string, any>) {
    return this._addLog(message, 'info', 'user', details);
  }

  // API Connection Logging
  logAPIRequest(endpoint: string, method: string, duration: number, statusCode: number, details?: Record<string, any>) {
    const message = `${method} ${endpoint} - ${statusCode} (${duration}ms)`;
    const level = statusCode >= 400 ? 'error' : statusCode >= 300 ? 'warning' : 'info';

    return this._addLog(message, level, 'api', {
      ...details,
      endpoint,
      method,
      duration,
      statusCode,
      timestamp: new Date().toISOString(),
    });
  }

  // Authentication Logging
  logAuth(
    action: 'login' | 'logout' | 'token_refresh' | 'key_validation',
    success: boolean,
    details?: Record<string, any>,
  ) {
    const message = `Auth ${action} - ${success ? 'Success' : 'Failed'}`;
    const level = success ? 'info' : 'error';

    return this._addLog(message, level, 'auth', {
      ...details,
      action,
      success,
      timestamp: new Date().toISOString(),
    });
  }

  // Network Status Logging
  logNetworkStatus(status: 'online' | 'offline' | 'reconnecting' | 'connected', details?: Record<string, any>) {
    const message = `Network ${status}`;
    const level = status === 'offline' ? 'error' : status === 'reconnecting' ? 'warning' : 'info';

    return this._addLog(message, level, 'network', {
      ...details,
      status,
      timestamp: new Date().toISOString(),
    });
  }

  // Database Operations Logging
  logDatabase(operation: string, success: boolean, duration: number, details?: Record<string, any>) {
    const message = `DB ${operation} - ${success ? 'Success' : 'Failed'} (${duration}ms)`;
    const level = success ? 'info' : 'error';

    return this._addLog(message, level, 'database', {
      ...details,
      operation,
      success,
      duration,
      timestamp: new Date().toISOString(),
    });
  }

  // Error events
  logError(message: string, error?: Error | unknown, details?: Record<string, any>) {
    const errorDetails =
      error instanceof Error
        ? {
            name: error.name,
            message: error.message,
            stack: error.stack,
            ...details,
          }
        : { error, ...details };

    return this._addLog(message, 'error', 'error', errorDetails);
  }

  // Warning events
  logWarning(message: string, details?: Record<string, any>) {
    return this._addLog(message, 'warning', 'system', details);
  }

  // Debug events
  logDebug(message: string, details?: Record<string, any>) {
    return this._addLog(message, 'debug', 'system', details);
  }

  clearLogs() {
    this._logs.set({});
    this._saveLogs();
  }

  getLogs() {
    return Object.values(this._logs.get()).sort(
      (a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(),
    );
  }

  getFilteredLogs(level?: LogEntry['level'], category?: LogEntry['category'], searchQuery?: string) {
    return this.getLogs().filter((log) => {
      const matchesLevel = !level || level === 'debug' || log.level === level;
      const matchesCategory = !category || log.category === category;
      const matchesSearch =
        !searchQuery ||
        log.message.toLowerCase().includes(searchQuery.toLowerCase()) ||
        JSON.stringify(log.details).toLowerCase().includes(searchQuery.toLowerCase());

      return matchesLevel && matchesCategory && matchesSearch;
    });
  }

  markAsRead(logId: string) {
    this._readLogs.add(logId);
    this._saveReadLogs();
  }

  isRead(logId: string): boolean {
    return this._readLogs.has(logId);
  }

  clearReadLogs() {
    this._readLogs.clear();
    this._saveReadLogs();
  }

  // API interactions
  logApiCall(
    method: string,
    endpoint: string,
    statusCode: number,
    duration: number,
    requestData?: any,
    responseData?: any,
  ) {
    return this._addLog(
      `API ${method} ${endpoint}`,
      statusCode >= 400 ? 'error' : 'info',
      'api',
      {
        method,
        endpoint,
        statusCode,
        duration,
        request: requestData,
        response: responseData,
      },
      {
        component: 'api',
        action: method,
      },
    );
  }

  // Network operations
  logNetworkRequest(
    method: string,
    url: string,
    statusCode: number,
    duration: number,
    requestData?: any,
    responseData?: any,
  ) {
    return this._addLog(
      `${method} ${url}`,
      statusCode >= 400 ? 'error' : 'info',
      'network',
      {
        method,
        url,
        statusCode,
        duration,
        request: requestData,
        response: responseData,
      },
      {
        component: 'network',
        action: method,
      },
    );
  }

  // Authentication events
  logAuthEvent(event: string, success: boolean, details?: Record<string, any>) {
    return this._addLog(
      `Auth ${event} ${success ? 'succeeded' : 'failed'}`,
      success ? 'info' : 'error',
      'auth',
      details,
      {
        component: 'auth',
        action: event,
      },
    );
  }

  // Performance tracking
  logPerformance(operation: string, duration: number, details?: Record<string, any>) {
    return this._addLog(
      `Performance: ${operation}`,
      duration > 1000 ? 'warning' : 'info',
      'performance',
      {
        operation,
        duration,
        ...details,
      },
      {
        component: 'performance',
        action: 'metric',
      },
    );
  }

  // Error handling
  logErrorWithStack(error: Error, category: LogEntry['category'] = 'error', details?: Record<string, any>) {
    return this._addLog(
      error.message,
      'error',
      category,
      {
        ...details,
        name: error.name,
        stack: error.stack,
      },
      {
        component: category,
        action: 'error',
      },
    );
  }

  // Refresh logs (useful for real-time updates)
  refreshLogs() {
    const currentLogs = this._logs.get();
    this._logs.set({ ...currentLogs });
  }

  // Enhanced logging methods
  logInfo(message: string, details: LogDetails) {
    return this._addLog(message, 'info', 'system', details);
  }

  logSuccess(message: string, details: LogDetails) {
    return this._addLog(message, 'info', 'system', { ...details, success: true });
  }

  logApiRequest(
    method: string,
    url: string,
    details: {
      method: string;
      url: string;
      statusCode: number;
      duration: number;
      request: any;
      response: any;
    },
  ) {
    return this._addApiLog(`API ${method} ${url}`, method, url, details);
  }

  logSettingsChange(component: string, setting: string, oldValue: any, newValue: any) {
    return this._addLog(
      `Settings changed in ${component}: ${setting}`,
      'info',
      'settings',
      {
        setting,
        previousValue: oldValue,
        newValue,
      },
      {
        component,
        action: 'settings_change',
        previousValue: oldValue,
        newValue,
      },
    );
  }

  logFeatureToggle(featureId: string, enabled: boolean) {
    return this._addLog(
      `Feature ${featureId} ${enabled ? 'enabled' : 'disabled'}`,
      'info',
      'feature',
      { featureId, enabled },
      {
        component: 'features',
        action: 'feature_toggle',
      },
    );
  }

  logTaskOperation(taskId: string, operation: string, status: string, details?: any) {
    return this._addLog(
      `Task ${taskId}: ${operation} - ${status}`,
      'info',
      'task',
      { taskId, operation, status, ...details },
      {
        component: 'task-manager',
        action: 'task_operation',
      },
    );
  }

  logProviderAction(provider: string, action: string, success: boolean, details?: any) {
    return this._addLog(
      `Provider ${provider}: ${action} - ${success ? 'Success' : 'Failed'}`,
      success ? 'info' : 'error',
      'provider',
      { provider, action, success, ...details },
      {
        component: 'providers',
        action: 'provider_action',
      },
    );
  }

  logPerformanceMetric(component: string, operation: string, duration: number, details?: any) {
    return this._addLog(
      `Performance: ${component} - ${operation} took ${duration}ms`,
      duration > 1000 ? 'warning' : 'info',
      'performance',
      { component, operation, duration, ...details },
      {
        component,
        action: 'performance_metric',
      },
    );
  }
}

export const logStore = new LogStore();
