/**
 * TASKMATE - State Store
 * ======================
 * Centralized state management
 */

import { deepClone, deepMerge } from './utils.js';

class Store {
  constructor() {
    this.state = {
      // App state
      currentView: 'board',
      isOnline: navigator.onLine,
      isLoading: false,
      syncStatus: 'synced', // 'synced', 'syncing', 'offline', 'error'

      // User state
      currentUser: null,
      users: [],

      // Project state
      projects: [],
      currentProjectId: null,

      // Board state
      columns: [],
      tasks: [],
      labels: [],
      reminders: [],

      // Filters
      filters: {
        search: '',
        priority: '',
        assignee: '',
        label: '',
        dueDate: '',
        archived: false
      },

      // Server search result IDs (bypass client filter for these)
      searchResultIds: [],

      // Selection
      selectedTaskIds: [],

      // Modal state
      openModals: [],
      editingTask: null,

      // UI state
      dragState: null,
      contextMenu: null,

      // Undo stack
      undoStack: [],
      redoStack: [],
      maxUndoHistory: 50
    };

    this.subscribers = new Map();
    this.middlewares = [];
  }

  // Get current state
  getState() {
    return this.state;
  }

  // Get specific state path
  get(path) {
    return path.split('.').reduce((obj, key) => obj?.[key], this.state);
  }

  // Update state
  setState(updates, actionType = 'SET_STATE') {
    const prevState = deepClone(this.state);

    // Apply middlewares
    let processedUpdates = updates;
    for (const middleware of this.middlewares) {
      processedUpdates = middleware(prevState, processedUpdates, actionType);
    }

    // Merge updates
    this.state = deepMerge(this.state, processedUpdates);

    // Notify subscribers
    this.notifySubscribers(prevState, this.state, actionType);

    return this.state;
  }

  // Set specific state path
  set(path, value, actionType) {
    const keys = path.split('.');
    const updates = keys.reduceRight((acc, key) => ({ [key]: acc }), value);
    return this.setState(updates, actionType || `SET_${path.toUpperCase()}`);
  }

  // Subscribe to state changes
  subscribe(selector, callback, immediate = false) {
    const id = Symbol();

    this.subscribers.set(id, { selector, callback });

    if (immediate) {
      const currentValue = typeof selector === 'function'
        ? selector(this.state)
        : this.get(selector);
      callback(currentValue, currentValue);
    }

    // Return unsubscribe function
    return () => this.subscribers.delete(id);
  }

  // Notify subscribers of changes
  notifySubscribers(prevState, newState, actionType) {
    this.subscribers.forEach(({ selector, callback }) => {
      const prevValue = typeof selector === 'function'
        ? selector(prevState)
        : selector.split('.').reduce((obj, key) => obj?.[key], prevState);

      const newValue = typeof selector === 'function'
        ? selector(newState)
        : selector.split('.').reduce((obj, key) => obj?.[key], newState);

      // Only call if value changed
      if (JSON.stringify(prevValue) !== JSON.stringify(newValue)) {
        callback(newValue, prevValue, actionType);
      }
    });
  }

  // Add middleware
  use(middleware) {
    this.middlewares.push(middleware);
  }

  // Reset state
  reset() {
    this.state = {
      currentView: 'board',
      isOnline: navigator.onLine,
      isLoading: false,
      syncStatus: 'synced',
      currentUser: null,
      users: [],
      projects: [],
      currentProjectId: null,
      columns: [],
      tasks: [],
      labels: [],
      filters: {
        search: '',
        priority: '',
        assignee: '',
        label: '',
        dueDate: '',
        archived: false
      },
      searchResultIds: [],
      selectedTaskIds: [],
      openModals: [],
      editingTask: null,
      dragState: null,
      contextMenu: null,
      undoStack: [],
      redoStack: [],
      maxUndoHistory: 50
    };
  }

  // =====================
  // PROJECT ACTIONS
  // =====================

  setProjects(projects) {
    this.setState({ projects }, 'SET_PROJECTS');
  }

  addProject(project) {
    this.setState({
      projects: [...this.state.projects, project]
    }, 'ADD_PROJECT');
  }

  updateProject(projectId, updates) {
    this.setState({
      projects: this.state.projects.map(p =>
        p.id === projectId ? { ...p, ...updates } : p
      )
    }, 'UPDATE_PROJECT');
  }

  removeProject(projectId) {
    this.setState({
      projects: this.state.projects.filter(p => p.id !== projectId)
    }, 'REMOVE_PROJECT');
  }

  setCurrentProject(projectId) {
    this.setState({ currentProjectId: projectId }, 'SET_CURRENT_PROJECT');
    localStorage.setItem('current_project_id', projectId);
  }

  getCurrentProject() {
    return this.state.projects.find(p => p.id === this.state.currentProjectId);
  }

  // =====================
  // COLUMN ACTIONS
  // =====================

  setColumns(columns) {
    this.setState({ columns }, 'SET_COLUMNS');
  }

  addColumn(column) {
    this.setState({
      columns: [...this.state.columns, column]
    }, 'ADD_COLUMN');
  }

  updateColumn(columnId, updates) {
    this.setState({
      columns: this.state.columns.map(c =>
        c.id === columnId ? { ...c, ...updates } : c
      )
    }, 'UPDATE_COLUMN');
  }

  removeColumn(columnId) {
    this.setState({
      columns: this.state.columns.filter(c => c.id !== columnId),
      tasks: this.state.tasks.filter(t => t.columnId !== columnId)
    }, 'REMOVE_COLUMN');
  }

  reorderColumns(columnIds) {
    const columnsMap = new Map(this.state.columns.map(c => [c.id, c]));
    const reordered = columnIds.map((id, index) => ({
      ...columnsMap.get(id),
      position: index
    }));

    this.setState({ columns: reordered }, 'REORDER_COLUMNS');
  }

  // =====================
  // TASK ACTIONS
  // =====================

  setTasks(tasks) {
    this.setState({ tasks }, 'SET_TASKS');
  }

  addTask(task) {
    this.setState({
      tasks: [...this.state.tasks, task]
    }, 'ADD_TASK');
  }

  updateTask(taskId, updates) {
    this.setState({
      tasks: this.state.tasks.map(t =>
        t.id === taskId ? { ...t, ...updates } : t
      )
    }, 'UPDATE_TASK');
  }

  removeTask(taskId) {
    this.setState({
      tasks: this.state.tasks.filter(t => t.id !== taskId),
      selectedTaskIds: this.state.selectedTaskIds.filter(id => id !== taskId)
    }, 'REMOVE_TASK');
  }

  moveTask(taskId, columnId, position) {
    const tasks = [...this.state.tasks];
    const taskIndex = tasks.findIndex(t => t.id === taskId);

    if (taskIndex === -1) return;

    const task = { ...tasks[taskIndex], columnId: columnId, position };
    tasks.splice(taskIndex, 1);

    // Find insert position
    const columnTasks = tasks.filter(t => t.columnId === columnId);
    const insertIndex = tasks.findIndex(t => t.columnId === columnId && t.position >= position);

    if (insertIndex === -1) {
      tasks.push(task);
    } else {
      tasks.splice(insertIndex, 0, task);
    }

    // Recalculate positions
    let pos = 0;
    tasks.forEach(t => {
      if (t.columnId === columnId) {
        t.position = pos++;
      }
    });

    this.setState({ tasks }, 'MOVE_TASK');
  }

  getTaskById(taskId) {
    return this.state.tasks.find(t => t.id === taskId);
  }

  getTasksByColumn(columnId) {
    // Priority order: high (0) > medium (1) > low (2)
    const priorityOrder = { high: 0, medium: 1, low: 2 };

    return this.state.tasks
      .filter(t => t.columnId === columnId && !t.archived)
      .sort((a, b) => {
        // 1. First by position (manual drag&drop sorting)
        const posA = a.position ?? 999999;
        const posB = b.position ?? 999999;
        if (posA !== posB) return posA - posB;

        // 2. Then by priority (high > medium > low)
        const priA = priorityOrder[a.priority] ?? 1;
        const priB = priorityOrder[b.priority] ?? 1;
        if (priA !== priB) return priA - priB;

        // 3. Then by creation date (older first)
        const dateA = new Date(a.createdAt || 0).getTime();
        const dateB = new Date(b.createdAt || 0).getTime();
        return dateA - dateB;
      });
  }

  // =====================
  // LABEL ACTIONS
  // =====================

  setLabels(labels) {
    this.setState({ labels }, 'SET_LABELS');
  }

  addLabel(label) {
    this.setState({
      labels: [...this.state.labels, label]
    }, 'ADD_LABEL');
  }

  updateLabel(labelId, updates) {
    this.setState({
      labels: this.state.labels.map(l =>
        l.id === labelId ? { ...l, ...updates } : l
      )
    }, 'UPDATE_LABEL');
  }

  removeLabel(labelId) {
    this.setState({
      labels: this.state.labels.filter(l => l.id !== labelId)
    }, 'REMOVE_LABEL');
  }

  // =====================
  // REMINDER ACTIONS  
  // =====================

  setReminders(reminders) {
    this.setState({ reminders }, 'SET_REMINDERS');
  }

  addReminder(reminder) {
    this.setState({
      reminders: [...this.state.reminders, reminder]
    }, 'ADD_REMINDER');
  }

  updateReminder(reminderId, updates) {
    this.setState({
      reminders: this.state.reminders.map(r =>
        r.id === reminderId ? { ...r, ...updates } : r
      )
    }, 'UPDATE_REMINDER');
  }

  removeReminder(reminderId) {
    this.setState({
      reminders: this.state.reminders.filter(r => r.id !== reminderId)
    }, 'REMOVE_REMINDER');
  }

  // =====================
  // FILTER ACTIONS
  // =====================

  setFilter(key, value) {
    this.setState({
      filters: { ...this.state.filters, [key]: value }
    }, 'SET_FILTER');
  }

  setFilters(filters) {
    this.setState({
      filters: { ...this.state.filters, ...filters }
    }, 'SET_FILTERS');
  }

  resetFilters() {
    this.setState({
      filters: {
        search: '',
        priority: '',
        assignee: '',
        label: '',
        dueDate: '',
        archived: false
      }
    }, 'RESET_FILTERS');
  }

  // =====================
  // SELECTION ACTIONS
  // =====================

  selectTask(taskId, multi = false) {
    if (multi) {
      const selected = this.state.selectedTaskIds.includes(taskId)
        ? this.state.selectedTaskIds.filter(id => id !== taskId)
        : [...this.state.selectedTaskIds, taskId];

      this.setState({ selectedTaskIds: selected }, 'SELECT_TASK');
    } else {
      this.setState({ selectedTaskIds: [taskId] }, 'SELECT_TASK');
    }
  }

  deselectTask(taskId) {
    this.setState({
      selectedTaskIds: this.state.selectedTaskIds.filter(id => id !== taskId)
    }, 'DESELECT_TASK');
  }

  clearSelection() {
    this.setState({ selectedTaskIds: [] }, 'CLEAR_SELECTION');
  }

  selectAllInColumn(columnId) {
    const taskIds = this.getTasksByColumn(columnId).map(t => t.id);
    this.setState({ selectedTaskIds: taskIds }, 'SELECT_ALL_IN_COLUMN');
  }

  // =====================
  // UI STATE ACTIONS
  // =====================

  setCurrentView(view) {
    this.setState({ currentView: view }, 'SET_VIEW');
  }

  setLoading(isLoading) {
    this.setState({ isLoading }, 'SET_LOADING');
  }

  setOnline(isOnline) {
    this.setState({
      isOnline,
      syncStatus: isOnline ? 'synced' : 'offline'
    }, 'SET_ONLINE');
  }

  setSyncStatus(status) {
    this.setState({ syncStatus: status }, 'SET_SYNC_STATUS');
  }

  setDragState(dragState) {
    this.setState({ dragState }, 'SET_DRAG_STATE');
  }

  setEditingTask(task) {
    this.setState({ editingTask: task }, 'SET_EDITING_TASK');
  }

  // =====================
  // MODAL ACTIONS
  // =====================

  openModal(modalId) {
    if (!this.state.openModals.includes(modalId)) {
      this.setState({
        openModals: [...this.state.openModals, modalId]
      }, 'OPEN_MODAL');
    }
  }

  closeModal(modalId) {
    this.setState({
      openModals: this.state.openModals.filter(id => id !== modalId)
    }, 'CLOSE_MODAL');
  }

  closeAllModals() {
    this.setState({ openModals: [] }, 'CLOSE_ALL_MODALS');
  }

  isModalOpen(modalId) {
    return this.state.openModals.includes(modalId);
  }

  // =====================
  // UNDO/REDO ACTIONS
  // =====================

  pushUndo(action) {
    const undoStack = [...this.state.undoStack, action];

    // Limit stack size
    if (undoStack.length > this.state.maxUndoHistory) {
      undoStack.shift();
    }

    this.setState({
      undoStack,
      redoStack: [] // Clear redo stack on new action
    }, 'PUSH_UNDO');
  }

  popUndo() {
    if (this.state.undoStack.length === 0) return null;

    const undoStack = [...this.state.undoStack];
    const action = undoStack.pop();

    this.setState({
      undoStack,
      redoStack: [...this.state.redoStack, action]
    }, 'POP_UNDO');

    return action;
  }

  popRedo() {
    if (this.state.redoStack.length === 0) return null;

    const redoStack = [...this.state.redoStack];
    const action = redoStack.pop();

    this.setState({
      redoStack,
      undoStack: [...this.state.undoStack, action]
    }, 'POP_REDO');

    return action;
  }

  canUndo() {
    return this.state.undoStack.length > 0;
  }

  canRedo() {
    return this.state.redoStack.length > 0;
  }

  // =====================
  // USER ACTIONS
  // =====================

  setCurrentUser(user) {
    this.setState({ currentUser: user }, 'SET_CURRENT_USER');
  }

  setUsers(users) {
    this.setState({ users }, 'SET_USERS');
  }

  getUserById(userId) {
    return this.state.users.find(u => u.id === userId);
  }
}

// Create singleton instance
const store = new Store();

// Debug middleware (only in development)
if (window.location.hostname === 'localhost') {
  store.use((prevState, updates, actionType) => {
    console.log(`[Store] ${actionType}:`, updates);
    return updates;
  });
}

// Persistence middleware for filters (EXCLUDING search to prevent "hanging" search)
store.subscribe('filters', (filters) => {
  // Save filters WITHOUT search - search should not persist across sessions
  const { search, ...filtersToSave } = filters;
  localStorage.setItem('task_filters', JSON.stringify(filtersToSave));
});

// Load persisted filters
const savedFilters = localStorage.getItem('task_filters');
if (savedFilters) {
  try {
    const parsed = JSON.parse(savedFilters);
    // Ensure search is always empty on load
    store.setFilters({ ...parsed, search: '' });
  } catch (e) {
    // Ignore parse errors
  }
}

export default store;
