/**
 * TASKMATE - Real-time Sync Module
 * =================================
 * WebSocket synchronization with Socket.io
 */

import store from './store.js';
import api from './api.js';

class SyncManager {
  constructor() {
    this.socket = null;
    this.isConnected = false;
    this.reconnectAttempts = 0;
    this.maxReconnectAttempts = 10;
    this.reconnectDelay = 1000;
    this.pendingOperations = [];
    this.operationQueue = [];
    this.isProcessingQueue = false;
  }

  // Initialize Socket.io connection
  async connect() {
    // Verhindere doppelte Verbindungen (auch während des Verbindungsaufbaus)
    if (this.socket) {
      console.log('[Sync] Socket already exists, skipping connect');
      return;
    }

    const token = api.getToken();
    if (!token) {
      console.log('[Sync] No auth token, skipping connection');
      return;
    }

    try {
      // Socket.io is loaded globally via script tag
      if (typeof io === 'undefined') {
        throw new Error('Socket.io not loaded');
      }

      this.socket = io({
        auth: { token },
        reconnection: true,
        reconnectionAttempts: this.maxReconnectAttempts,
        reconnectionDelay: this.reconnectDelay,
        reconnectionDelayMax: 10000,
        timeout: 20000
      });

      this.setupEventListeners();

      // Update body offline class based on sync status
      store.subscribe('syncStatus', () => {
        const status = store.get('syncStatus');
        if (status === 'offline' || status === 'error') {
          document.body.classList.add('offline');
        } else {
          document.body.classList.remove('offline');
        }
      });
    } catch (error) {
      console.error('[Sync] Failed to connect:', error);
      store.setSyncStatus('error');
    }
  }

  // Setup socket event listeners
  setupEventListeners() {
    if (!this.socket) return;

    // Connection events
    this.socket.on('connect', () => {
      console.log('[Sync] Connected');
      this.isConnected = true;
      this.reconnectAttempts = 0;
      store.setSyncStatus('synced');

      // Join current project room
      const projectId = store.get('currentProjectId');
      if (projectId) {
        this.joinProject(projectId);
      }

      // Process pending operations
      this.processPendingOperations();
    });

    this.socket.on('disconnect', (reason) => {
      console.log('[Sync] Disconnected:', reason);
      this.isConnected = false;

      if (reason === 'io server disconnect') {
        // Server initiated disconnect, try reconnecting
        this.socket.connect();
      }

      store.setSyncStatus('offline');
    });

    this.socket.on('connect_error', (error) => {
      console.error('[Sync] Connection error:', error);
      this.reconnectAttempts++;

      if (this.reconnectAttempts >= this.maxReconnectAttempts) {
        store.setSyncStatus('error');
      } else {
        store.setSyncStatus('offline');
      }
    });

    // Auth error
    this.socket.on('error', (error) => {
      console.error('[Sync] Socket error:', error);

      if (error.type === 'auth') {
        // Nur ausloggen wenn wir wirklich nicht eingeloggt sind
        // (verhindert Logout durch alte Socket-Verbindungen nach neuem Login)
        const currentToken = localStorage.getItem('auth_token');
        if (!currentToken) {
          console.log('[Sync] Auth error and no token, triggering logout');
          window.dispatchEvent(new CustomEvent('auth:logout'));
        } else {
          console.log('[Sync] Auth error ignored - new login occurred');
        }
      }
    });

    // Data sync events
    this.socket.on('project:updated', (data) => {
      this.handleProjectUpdated(data);
    });

    this.socket.on('column:created', (data) => {
      this.handleColumnCreated(data);
    });

    this.socket.on('column:updated', (data) => {
      this.handleColumnUpdated(data);
    });

    this.socket.on('column:deleted', (data) => {
      this.handleColumnDeleted(data);
    });

    this.socket.on('column:reordered', (data) => {
      this.handleColumnsReordered(data);
    });

    this.socket.on('task:created', (data) => {
      this.handleTaskCreated(data);
    });

    this.socket.on('task:updated', (data) => {
      this.handleTaskUpdated(data);
    });

    this.socket.on('task:deleted', (data) => {
      this.handleTaskDeleted(data);
    });

    this.socket.on('task:moved', (data) => {
      this.handleTaskMoved(data);
    });

    this.socket.on('task:archived', (data) => {
      this.handleTaskArchived(data);
    });

    this.socket.on('label:created', (data) => {
      this.handleLabelCreated(data);
    });

    this.socket.on('label:updated', (data) => {
      this.handleLabelUpdated(data);
    });

    this.socket.on('label:deleted', (data) => {
      this.handleLabelDeleted(data);
    });

    this.socket.on('comment:created', (data) => {
      this.handleCommentCreated(data);
    });

    this.socket.on('subtask:updated', (data) => {
      this.handleSubtaskUpdated(data);
    });

    // User presence events
    this.socket.on('user:joined', (data) => {
      this.handleUserJoined(data);
    });

    this.socket.on('user:left', (data) => {
      this.handleUserLeft(data);
    });

    this.socket.on('user:typing', (data) => {
      this.handleUserTyping(data);
    });

    // Notification events
    this.socket.on('notification:new', (data) => {
      this.handleNotificationNew(data);
    });

    this.socket.on('notification:count', (data) => {
      this.handleNotificationCount(data);
    });

    this.socket.on('notification:deleted', (data) => {
      this.handleNotificationDeleted(data);
    });
  }

  // Disconnect socket
  disconnect() {
    if (this.socket) {
      this.socket.disconnect();
      this.socket = null;
      this.isConnected = false;
    }
  }

  // Join project room
  joinProject(projectId) {
    if (!this.socket?.connected) return;

    this.socket.emit('project:join', { projectId });
    console.log('[Sync] Joined project:', projectId);
  }

  // Leave project room
  leaveProject(projectId) {
    if (!this.socket?.connected) return;

    this.socket.emit('project:leave', { projectId });
    console.log('[Sync] Left project:', projectId);
  }

  // Emit event with queueing for offline support
  emit(event, data) {
    if (this.socket?.connected) {
      this.socket.emit(event, data);
      return true;
    }

    // Queue for later
    this.pendingOperations.push({ event, data, timestamp: Date.now() });
    store.setSyncStatus('offline');
    return false;
  }

  // Process pending operations when reconnected
  async processPendingOperations() {
    if (!this.socket?.connected || this.pendingOperations.length === 0) {
      return;
    }

    console.log(`[Sync] Processing ${this.pendingOperations.length} pending operations`);
    store.setSyncStatus('syncing');

    const operations = [...this.pendingOperations];
    this.pendingOperations = [];

    for (const op of operations) {
      try {
        this.socket.emit(op.event, op.data);
        await this.delay(100); // Small delay between operations
      } catch (error) {
        console.error('[Sync] Failed to process operation:', error);
        // Re-queue failed operation
        this.pendingOperations.push(op);
      }
    }

    store.setSyncStatus(this.pendingOperations.length > 0 ? 'offline' : 'synced');
  }

  // Helper delay function
  delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  // =====================
  // EVENT HANDLERS
  // =====================

  handleProjectUpdated(data) {
    const { project, userId } = data;

    // Ignore own updates
    if (userId === store.get('currentUser')?.id) return;

    store.updateProject(project.id, project);

    this.showNotification('Projekt aktualisiert', `${data.username} hat das Projekt aktualisiert`);
  }

  handleColumnCreated(data) {
    const { column, userId } = data;

    if (userId === store.get('currentUser')?.id) return;

    store.addColumn(column);

    this.showNotification('Neue Spalte', `${data.username} hat eine Spalte erstellt`);
  }

  handleColumnUpdated(data) {
    const { column, userId } = data;

    if (userId === store.get('currentUser')?.id) return;

    store.updateColumn(column.id, column);
  }

  handleColumnDeleted(data) {
    const { columnId, userId } = data;

    if (userId === store.get('currentUser')?.id) return;

    store.removeColumn(columnId);
  }

  handleColumnsReordered(data) {
    const { columnIds, userId } = data;

    if (userId === store.get('currentUser')?.id) return;

    store.reorderColumns(columnIds);
  }

  handleTaskCreated(data) {
    const { task, userId, username } = data;

    if (userId === store.get('currentUser')?.id) return;

    store.addTask(task);

    this.showNotification('Neue Aufgabe', `${username} hat "${task.title}" erstellt`);
  }

  handleTaskUpdated(data) {
    const { task, userId, changes } = data;

    if (userId === store.get('currentUser')?.id) return;

    store.updateTask(task.id, task);

    // Show notification for significant changes
    if (changes.includes('status') || changes.includes('assignee')) {
      this.showNotification('Aufgabe aktualisiert', `"${task.title}" wurde aktualisiert`);
    }
  }

  handleTaskDeleted(data) {
    const { taskId, userId, taskTitle } = data;

    if (userId === store.get('currentUser')?.id) return;

    store.removeTask(taskId);

    this.showNotification('Aufgabe gelöscht', `"${taskTitle}" wurde gelöscht`);
  }

  handleTaskMoved(data) {
    const { taskId, columnId, position, userId } = data;

    if (userId === store.get('currentUser')?.id) return;

    store.moveTask(taskId, columnId, position);
  }

  handleTaskArchived(data) {
    const { id, archived, task, userId } = data;

    if (userId && userId === store.get('currentUser')?.id) return;

    if (archived) {
      // Archivieren: aus Board-Sicht entfernen (Task bleibt in DB)
      store.removeTask(id);
    } else {
      // Wiederherstellen: Task zurück in den Store bringen
      if (task) {
        const existing = store.getTaskById ? store.getTaskById(id) : null;
        if (existing) {
          store.updateTask(id, { ...task, archived: false });
        } else {
          store.addTask({ ...task, archived: false });
        }
      }
    }
  }

  handleLabelCreated(data) {
    const { label, userId } = data;

    if (userId === store.get('currentUser')?.id) return;

    store.addLabel(label);
  }

  handleLabelUpdated(data) {
    const { label, userId } = data;

    if (userId === store.get('currentUser')?.id) return;

    store.updateLabel(label.id, label);
  }

  handleLabelDeleted(data) {
    const { labelId, userId } = data;

    if (userId === store.get('currentUser')?.id) return;

    store.removeLabel(labelId);
  }

  handleCommentCreated(data) {
    const { taskId, comment, userId, username } = data;

    if (userId === store.get('currentUser')?.id) return;

    // Update task comment count
    const task = store.getTaskById(taskId);
    if (task) {
      store.updateTask(taskId, {
        commentCount: (task.commentCount || 0) + 1
      });
    }

    // Check for mention
    const currentUser = store.get('currentUser');
    if (comment.content.includes(`@${currentUser?.username}`)) {
      this.showNotification(
        'Du wurdest erwähnt',
        `${username} hat dich in "${task?.title}" erwähnt`,
        'mention'
      );
    }
  }

  handleSubtaskUpdated(data) {
    const { taskId, subtask, userId } = data;

    if (userId === store.get('currentUser')?.id) return;

    // Trigger task refresh if viewing this task
    const editingTask = store.get('editingTask');
    if (editingTask?.id === taskId) {
      window.dispatchEvent(new CustomEvent('task:refresh', {
        detail: { taskId }
      }));
    }
  }

  handleUserJoined(data) {
    const { userId, username } = data;

    if (userId === store.get('currentUser')?.id) return;

    this.showNotification('Benutzer online', `${username} ist jetzt online`, 'info');
  }

  handleUserLeft(data) {
    const { userId, username } = data;

    if (userId === store.get('currentUser')?.id) return;

    // Optional: show notification
  }

  handleUserTyping(data) {
    const { userId, taskId, isTyping } = data;

    if (userId === store.get('currentUser')?.id) return;

    window.dispatchEvent(new CustomEvent('user:typing', {
      detail: { userId, taskId, isTyping }
    }));
  }

  handleNotificationNew(data) {
    const { notification } = data;
    window.dispatchEvent(new CustomEvent('notification:new', {
      detail: { notification }
    }));
  }

  handleNotificationCount(data) {
    const { count } = data;
    window.dispatchEvent(new CustomEvent('notification:count', {
      detail: { count }
    }));
  }

  handleNotificationDeleted(data) {
    const { notificationId } = data;
    window.dispatchEvent(new CustomEvent('notification:deleted', {
      detail: { notificationId }
    }));
  }

  // =====================
  // OUTGOING EVENTS
  // =====================

  notifyTaskCreated(task) {
    this.emit('task:create', {
      task,
      projectId: store.get('currentProjectId')
    });
  }

  notifyTaskUpdated(task, changes) {
    this.emit('task:update', {
      task,
      changes,
      projectId: store.get('currentProjectId')
    });
  }

  notifyTaskDeleted(taskId, taskTitle) {
    this.emit('task:delete', {
      taskId,
      taskTitle,
      projectId: store.get('currentProjectId')
    });
  }

  notifyTaskMoved(taskId, columnId, position) {
    this.emit('task:move', {
      taskId,
      columnId,
      position,
      projectId: store.get('currentProjectId')
    });
  }

  notifyColumnCreated(column) {
    this.emit('column:create', {
      column,
      projectId: store.get('currentProjectId')
    });
  }

  notifyColumnUpdated(column) {
    this.emit('column:update', {
      column,
      projectId: store.get('currentProjectId')
    });
  }

  notifyColumnDeleted(columnId) {
    this.emit('column:delete', {
      columnId,
      projectId: store.get('currentProjectId')
    });
  }

  notifyColumnsReordered(columnIds) {
    this.emit('column:reorder', {
      columnIds,
      projectId: store.get('currentProjectId')
    });
  }

  notifyTyping(taskId, isTyping) {
    this.emit('user:typing', {
      taskId,
      isTyping,
      projectId: store.get('currentProjectId')
    });
  }

  // =====================
  // NOTIFICATIONS
  // =====================

  showNotification(title, message, type = 'info') {
    window.dispatchEvent(new CustomEvent('toast:show', {
      detail: { title, message, type }
    }));
  }
}

// Create singleton instance
const syncManager = new SyncManager();

// Listen for auth events
// Hinweis: syncManager.connect() wird NICHT hier aufgerufen,
// sondern in app.js initializeApp() um doppelte Verbindungen zu vermeiden

window.addEventListener('auth:logout', () => {
  syncManager.disconnect();
});

// Listen for project changes
store.subscribe('currentProjectId', (newProjectId, oldProjectId) => {
  if (oldProjectId) {
    syncManager.leaveProject(oldProjectId);
  }
  if (newProjectId) {
    syncManager.joinProject(newProjectId);
  }
});

// Listen for online/offline events
window.addEventListener('online', () => {
  store.setOnline(true);
  syncManager.connect();
});

window.addEventListener('offline', () => {
  store.setOnline(false);
});

export default syncManager;
