/**
 * Fix Missing Lists Migration
 * 
 * This migration fixes the issue where cards have incorrect listId references
 * due to the per-swimlane lists change. It detects cards with mismatched
 * listId/swimlaneId and creates the missing lists.
 * 
 * Issue: When upgrading from v7.94 to v8.02, cards that were in different
 * swimlanes but shared the same list now have wrong listId references.
 * 
 * Example:
 * - Card1: listId: 'HB93dWNnY5bgYdtxc', swimlaneId: 'sK69SseWkh3tMbJvg'
 * - Card2: listId: 'HB93dWNnY5bgYdtxc', swimlaneId: 'XeecF9nZxGph4zcT4'
 * 
 * Card2 should have a different listId that corresponds to its swimlane.
 */

import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import { ReactiveCache } from '/imports/reactiveCache';
import Boards from '/models/boards';
import Lists from '/models/lists';
import Cards from '/models/cards';

class FixMissingListsMigration {
  constructor() {
    this.name = 'fix-missing-lists';
    this.version = 1;
  }

  /**
   * Check if migration is needed for a board
   */
  needsMigration(boardId) {
    try {
      const board = ReactiveCache.getBoard(boardId);
      if (!board) return false;

      // Check if board has already been processed
      if (board.fixMissingListsCompleted) {
        return false;
      }

      // Check if there are cards with mismatched listId/swimlaneId
      const cards = ReactiveCache.getCards({ boardId });
      const lists = ReactiveCache.getLists({ boardId });
      
      // Create a map of listId -> swimlaneId for existing lists
      const listSwimlaneMap = new Map();
      lists.forEach(list => {
        listSwimlaneMap.set(list._id, list.swimlaneId || '');
      });

      // Check for cards with mismatched listId/swimlaneId
      for (const card of cards) {
        const expectedSwimlaneId = listSwimlaneMap.get(card.listId);
        if (expectedSwimlaneId && expectedSwimlaneId !== card.swimlaneId) {
          if (process.env.DEBUG === 'true') {
            console.log(`Found mismatched card: ${card._id}, listId: ${card.listId}, card swimlaneId: ${card.swimlaneId}, list swimlaneId: ${expectedSwimlaneId}`);
          }
          return true;
        }
      }

      return false;
    } catch (error) {
      console.error('Error checking if migration is needed:', error);
      return false;
    }
  }

  /**
   * Execute the migration for a board
   */
  async executeMigration(boardId) {
    try {
      if (process.env.DEBUG === 'true') {
        console.log(`Starting fix missing lists migration for board ${boardId}`);
      }
      
      const board = ReactiveCache.getBoard(boardId);
      if (!board) {
        throw new Error(`Board ${boardId} not found`);
      }

      const cards = ReactiveCache.getCards({ boardId });
      const lists = ReactiveCache.getLists({ boardId });
      const swimlanes = ReactiveCache.getSwimlanes({ boardId });

      // Create maps for efficient lookup
      const listSwimlaneMap = new Map();
      const swimlaneListsMap = new Map();
      
      lists.forEach(list => {
        listSwimlaneMap.set(list._id, list.swimlaneId || '');
        if (!swimlaneListsMap.has(list.swimlaneId || '')) {
          swimlaneListsMap.set(list.swimlaneId || '', []);
        }
        swimlaneListsMap.get(list.swimlaneId || '').push(list);
      });

      // Group cards by swimlaneId
      const cardsBySwimlane = new Map();
      cards.forEach(card => {
        if (!cardsBySwimlane.has(card.swimlaneId)) {
          cardsBySwimlane.set(card.swimlaneId, []);
        }
        cardsBySwimlane.get(card.swimlaneId).push(card);
      });

      let createdLists = 0;
      let updatedCards = 0;

      // Process each swimlane
      for (const [swimlaneId, swimlaneCards] of cardsBySwimlane) {
        if (!swimlaneId) continue;

        // Get existing lists for this swimlane
        const existingLists = swimlaneListsMap.get(swimlaneId) || [];
        const existingListTitles = new Set(existingLists.map(list => list.title));

        // Group cards by their current listId
        const cardsByListId = new Map();
        swimlaneCards.forEach(card => {
          if (!cardsByListId.has(card.listId)) {
            cardsByListId.set(card.listId, []);
          }
          cardsByListId.get(card.listId).push(card);
        });

        // For each listId used by cards in this swimlane
        for (const [listId, cardsInList] of cardsByListId) {
          const originalList = lists.find(l => l._id === listId);
          if (!originalList) continue;

          // Check if this list's swimlaneId matches the card's swimlaneId
          const listSwimlaneId = listSwimlaneMap.get(listId);
          if (listSwimlaneId === swimlaneId) {
            // List is already correctly assigned to this swimlane
            continue;
          }

          // Check if we already have a list with the same title in this swimlane
          let targetList = existingLists.find(list => list.title === originalList.title);
          
          if (!targetList) {
            // Create a new list for this swimlane
            const newListData = {
              title: originalList.title,
              boardId: boardId,
              swimlaneId: swimlaneId,
              sort: originalList.sort || 0,
              archived: originalList.archived || false,
              createdAt: new Date(),
              modifiedAt: new Date(),
              type: originalList.type || 'list'
            };

            // Copy other properties if they exist
            if (originalList.color) newListData.color = originalList.color;
            if (originalList.wipLimit) newListData.wipLimit = originalList.wipLimit;
            if (originalList.wipLimitEnabled) newListData.wipLimitEnabled = originalList.wipLimitEnabled;
            if (originalList.wipLimitSoft) newListData.wipLimitSoft = originalList.wipLimitSoft;
            if (originalList.starred) newListData.starred = originalList.starred;
            if (originalList.collapsed) newListData.collapsed = originalList.collapsed;

            // Insert the new list
            const newListId = Lists.insert(newListData);
            targetList = { _id: newListId, ...newListData };
            createdLists++;
            
            if (process.env.DEBUG === 'true') {
              console.log(`Created new list "${originalList.title}" for swimlane ${swimlaneId}`);
            }
          }

          // Update all cards in this group to use the correct listId
          for (const card of cardsInList) {
            Cards.update(card._id, {
              $set: {
                listId: targetList._id,
                modifiedAt: new Date()
              }
            });
            updatedCards++;
          }
        }
      }

      // Mark board as processed
      Boards.update(boardId, {
        $set: {
          fixMissingListsCompleted: true,
          fixMissingListsCompletedAt: new Date()
        }
      });

      if (process.env.DEBUG === 'true') {
        console.log(`Fix missing lists migration completed for board ${boardId}: created ${createdLists} lists, updated ${updatedCards} cards`);
      }
      
      return {
        success: true,
        createdLists,
        updatedCards
      };

    } catch (error) {
      console.error(`Error executing fix missing lists migration for board ${boardId}:`, error);
      throw error;
    }
  }

  /**
   * Get migration status for a board
   */
  getMigrationStatus(boardId) {
    try {
      const board = ReactiveCache.getBoard(boardId);
      if (!board) {
        return { status: 'board_not_found' };
      }

      if (board.fixMissingListsCompleted) {
        return { 
          status: 'completed',
          completedAt: board.fixMissingListsCompletedAt
        };
      }

      const needsMigration = this.needsMigration(boardId);
      return {
        status: needsMigration ? 'needed' : 'not_needed'
      };

    } catch (error) {
      console.error('Error getting migration status:', error);
      return { status: 'error', error: error.message };
    }
  }
}

// Export singleton instance
export const fixMissingListsMigration = new FixMissingListsMigration();

// Meteor methods
Meteor.methods({
  'fixMissingListsMigration.check'(boardId) {
    check(boardId, String);
    
    if (!this.userId) {
      throw new Meteor.Error('not-authorized');
    }
    
    return fixMissingListsMigration.getMigrationStatus(boardId);
  },

  'fixMissingListsMigration.execute'(boardId) {
    check(boardId, String);
    
    if (!this.userId) {
      throw new Meteor.Error('not-authorized');
    }
    
    return fixMissingListsMigration.executeMigration(boardId);
  },

  'fixMissingListsMigration.needsMigration'(boardId) {
    check(boardId, String);
    
    if (!this.userId) {
      throw new Meteor.Error('not-authorized');
    }
    
    return fixMissingListsMigration.needsMigration(boardId);
  }
});
