import { Nupst } from '../nupst.js';
import { logger } from '../logger.js';
import * as helpers from '../helpers/index.js';
import { type IGroupConfig } from '../daemon.js';

/**
 * Class for handling group-related CLI commands
 * Provides interface for managing UPS groups
 */
export class GroupHandler {
  private readonly nupst: Nupst;

  /**
   * Create a new Group handler
   * @param nupst Reference to the main Nupst instance
   */
  constructor(nupst: Nupst) {
    this.nupst = nupst;
  }

  /**
   * List all UPS groups
   */
  public async list(): Promise<void> {
    try {
      // Try to load configuration
      try {
        await this.nupst.getDaemon().loadConfig();
      } catch (error) {
        const errorBoxWidth = 45;
        logger.logBoxTitle('Configuration Error', errorBoxWidth);
        logger.logBoxLine('No configuration found.');
        logger.logBoxLine("Please run 'nupst setup' first to create a configuration.");
        logger.logBoxEnd();
        return;
      }
      
      // Get current configuration
      const config = this.nupst.getDaemon().getConfig();
      
      // Check if multi-UPS config
      if (!config.groups || !Array.isArray(config.groups)) {
        // Legacy or missing groups configuration
        const boxWidth = 45;
        logger.logBoxTitle('UPS Groups', boxWidth);
        logger.logBoxLine('No groups configured.');
        logger.logBoxLine('Use "nupst group add" to add a UPS group.');
        logger.logBoxEnd();
        return;
      }
      
      // Display group list
      const boxWidth = 60;
      logger.logBoxTitle('UPS Groups', boxWidth);
      
      if (config.groups.length === 0) {
        logger.logBoxLine('No UPS groups configured.');
        logger.logBoxLine('Use "nupst group add" to add a UPS group.');
      } else {
        logger.logBoxLine(`Found ${config.groups.length} group(s)`);
        logger.logBoxLine('');
        logger.logBoxLine('ID         | Name                 | Mode         | UPS Devices');
        logger.logBoxLine('-----------+----------------------+--------------+----------------');
        
        for (const group of config.groups) {
          const id = group.id.padEnd(10, ' ').substring(0, 10);
          const name = (group.name || '').padEnd(20, ' ').substring(0, 20);
          const mode = (group.mode || 'unknown').padEnd(12, ' ').substring(0, 12);
          
          // Count UPS devices in this group
          const upsInGroup = config.upsDevices.filter(ups => ups.groups.includes(group.id));
          const upsCount = upsInGroup.length;
          const upsNames = upsInGroup.map(ups => ups.name).join(', ');
          
          logger.logBoxLine(`${id} | ${name} | ${mode} | ${upsCount > 0 ? upsNames : 'None'}`);
        }
      }
      
      logger.logBoxEnd();
    } catch (error) {
      logger.error(`Failed to list UPS groups: ${error.message}`);
    }
  }
  
  /**
   * Add a new UPS group
   */
  public async add(): Promise<void> {
    try {
      // Import readline module for user input
      const readline = await import('readline');

      const rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout,
      });

      // Helper function to prompt for input
      const prompt = (question: string): Promise<string> => {
        return new Promise((resolve) => {
          rl.question(question, (answer: string) => {
            resolve(answer);
          });
        });
      };

      try {
        // Try to load configuration
        try {
          await this.nupst.getDaemon().loadConfig();
        } catch (error) {
          logger.error('No configuration found. Please run "nupst setup" first to create a configuration.');
          return;
        }
        
        // Get current configuration
        const config = this.nupst.getDaemon().getConfig();
        
        // Initialize groups array if not exists
        if (!config.groups) {
          config.groups = [];
        }
        
        // Check if upsDevices is initialized
        if (!config.upsDevices) {
          config.upsDevices = [];
        }
        
        logger.log('\nNUPST Add Group');
        logger.log('==============\n');
        logger.log('This will guide you through creating a new UPS group.\n');
        
        // Generate a new unique group ID
        const groupId = helpers.shortId();
        
        // Get group name
        const name = await prompt('Group Name: ');
        
        // Get group mode
        const modeInput = await prompt('Group Mode (redundant/nonRedundant) [redundant]: ');
        const mode = modeInput.toLowerCase() === 'nonredundant' ? 'nonRedundant' : 'redundant';
        
        // Get optional description
        const description = await prompt('Group Description (optional): ');
        
        // Create the new group
        const newGroup: IGroupConfig = {
          id: groupId,
          name: name || `Group-${groupId}`,
          mode,
          description: description || undefined
        };
        
        // Add the group to the configuration
        config.groups.push(newGroup);
        
        // Save the configuration
        await this.nupst.getDaemon().saveConfig(config);
        
        // Display summary
        const boxWidth = 45;
        logger.logBoxTitle('Group Created', boxWidth);
        logger.logBoxLine(`ID: ${newGroup.id}`);
        logger.logBoxLine(`Name: ${newGroup.name}`);
        logger.logBoxLine(`Mode: ${newGroup.mode}`);
        if (newGroup.description) {
          logger.logBoxLine(`Description: ${newGroup.description}`);
        }
        logger.logBoxEnd();
        
        // Check if there are UPS devices to assign to this group
        if (config.upsDevices.length > 0) {
          const assignUps = await prompt('Would you like to assign UPS devices to this group now? (y/N): ');
          if (assignUps.toLowerCase() === 'y') {
            await this.assignUpsToGroup(newGroup.id, config, prompt);
            
            // Save again after assigning UPS devices
            await this.nupst.getDaemon().saveConfig(config);
          }
        }
        
        // Check if service is running and restart it if needed
        this.nupst.getUpsHandler().restartServiceIfRunning();
        
        logger.log('\nGroup setup complete!');
      } finally {
        rl.close();
      }
    } catch (error) {
      logger.error(`Add group error: ${error.message}`);
    }
  }
  
  /**
   * Edit an existing UPS group
   * @param groupId ID of the group to edit
   */
  public async edit(groupId: string): Promise<void> {
    try {
      // Import readline module for user input
      const readline = await import('readline');

      const rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout,
      });

      // Helper function to prompt for input
      const prompt = (question: string): Promise<string> => {
        return new Promise((resolve) => {
          rl.question(question, (answer: string) => {
            resolve(answer);
          });
        });
      };

      try {
        // Try to load configuration
        try {
          await this.nupst.getDaemon().loadConfig();
        } catch (error) {
          logger.error('No configuration found. Please run "nupst setup" first to create a configuration.');
          return;
        }
        
        // Get current configuration
        const config = this.nupst.getDaemon().getConfig();
        
        // Check if groups are initialized
        if (!config.groups || !Array.isArray(config.groups)) {
          logger.error('No groups configured. Please run "nupst group add" first to create a group.');
          return;
        }
        
        // Find the group to edit
        const groupIndex = config.groups.findIndex(group => group.id === groupId);
        if (groupIndex === -1) {
          logger.error(`Group with ID "${groupId}" not found.`);
          return;
        }
        
        const group = config.groups[groupIndex];
        
        logger.log(`\nNUPST Edit Group: ${group.name} (${group.id})`);
        logger.log('==============================================\n');
        
        // Edit group name
        const newName = await prompt(`Group Name [${group.name}]: `);
        if (newName.trim()) {
          group.name = newName;
        }
        
        // Edit group mode
        const currentMode = group.mode || 'redundant';
        const modeInput = await prompt(`Group Mode (redundant/nonRedundant) [${currentMode}]: `);
        if (modeInput.trim()) {
          group.mode = modeInput.toLowerCase() === 'nonredundant' ? 'nonRedundant' : 'redundant';
        }
        
        // Edit description
        const currentDesc = group.description || '';
        const newDesc = await prompt(`Group Description [${currentDesc}]: `);
        if (newDesc.trim() || newDesc === '') {
          group.description = newDesc.trim() || undefined;
        }
        
        // Update the group in the configuration
        config.groups[groupIndex] = group;
        
        // Save the configuration
        await this.nupst.getDaemon().saveConfig(config);
        
        // Display summary
        const boxWidth = 45;
        logger.logBoxTitle('Group Updated', boxWidth);
        logger.logBoxLine(`ID: ${group.id}`);
        logger.logBoxLine(`Name: ${group.name}`);
        logger.logBoxLine(`Mode: ${group.mode}`);
        if (group.description) {
          logger.logBoxLine(`Description: ${group.description}`);
        }
        logger.logBoxEnd();
        
        // Edit UPS assignments if requested
        const editAssignments = await prompt('Would you like to edit UPS assignments for this group? (y/N): ');
        if (editAssignments.toLowerCase() === 'y') {
          await this.assignUpsToGroup(group.id, config, prompt);
          
          // Save again after editing assignments
          await this.nupst.getDaemon().saveConfig(config);
        }
        
        // Check if service is running and restart it if needed
        this.nupst.getUpsHandler().restartServiceIfRunning();
        
        logger.log('\nGroup edit complete!');
      } finally {
        rl.close();
      }
    } catch (error) {
      logger.error(`Edit group error: ${error.message}`);
    }
  }
  
  /**
   * Delete an existing UPS group
   * @param groupId ID of the group to delete
   */
  public async delete(groupId: string): Promise<void> {
    try {
      // Try to load configuration
      try {
        await this.nupst.getDaemon().loadConfig();
      } catch (error) {
        logger.error('No configuration found. Please run "nupst setup" first to create a configuration.');
        return;
      }
      
      // Get current configuration
      const config = this.nupst.getDaemon().getConfig();
      
      // Check if groups are initialized
      if (!config.groups || !Array.isArray(config.groups)) {
        logger.error('No groups configured.');
        return;
      }
      
      // Find the group to delete
      const groupIndex = config.groups.findIndex(group => group.id === groupId);
      if (groupIndex === -1) {
        logger.error(`Group with ID "${groupId}" not found.`);
        return;
      }
      
      const groupToDelete = config.groups[groupIndex];
      
      // Get confirmation before deleting
      const readline = await import('readline');
      const rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout,
      });
      
      const confirm = await new Promise<string>(resolve => {
        rl.question(`Are you sure you want to delete group "${groupToDelete.name}" (${groupId})? [y/N]: `, answer => {
          resolve(answer.toLowerCase());
        });
      });
      
      rl.close();
      
      if (confirm !== 'y' && confirm !== 'yes') {
        logger.log('Deletion cancelled.');
        return;
      }
      
      // Remove this group from all UPS device group assignments
      if (config.upsDevices && Array.isArray(config.upsDevices)) {
        for (const ups of config.upsDevices) {
          const groupIndex = ups.groups.indexOf(groupId);
          if (groupIndex !== -1) {
            ups.groups.splice(groupIndex, 1);
          }
        }
      }
      
      // Remove the group from the array
      config.groups.splice(groupIndex, 1);
      
      // Save the configuration
      await this.nupst.getDaemon().saveConfig(config);
      
      logger.log(`Group "${groupToDelete.name}" (${groupId}) has been deleted.`);
      
      // Check if service is running and restart it if needed
      this.nupst.getUpsHandler().restartServiceIfRunning();
    } catch (error) {
      logger.error(`Failed to delete group: ${error.message}`);
    }
  }

  /**
   * Assign UPS devices to groups
   * @param ups UPS configuration to update
   * @param groups Available groups
   * @param prompt Function to prompt for user input
   */
  public async assignUpsToGroups(
    ups: any,
    groups: any[],
    prompt: (question: string) => Promise<string>
  ): Promise<void> {
    // Initialize groups array if it doesn't exist
    if (!ups.groups) {
      ups.groups = [];
    }
    
    // Show current group assignments
    logger.log('\nCurrent Group Assignments:');
    if (ups.groups && ups.groups.length > 0) {
      for (const groupId of ups.groups) {
        const group = groups.find(g => g.id === groupId);
        if (group) {
          logger.log(`- ${group.name} (${group.id})`);
        } else {
          logger.log(`- Unknown group (${groupId})`);
        }
      }
    } else {
      logger.log('- None');
    }
    
    // Show available groups
    logger.log('\nAvailable Groups:');
    if (groups.length === 0) {
      logger.log('- No groups available. Use "nupst group add" to create groups.');
      return;
    }
    
    for (let i = 0; i < groups.length; i++) {
      const group = groups[i];
      const assigned = ups.groups && ups.groups.includes(group.id);
      logger.log(`${i + 1}) ${group.name} (${group.id}) [${assigned ? 'Assigned' : 'Not Assigned'}]`);
    }
    
    // Prompt for group selection
    const selection = await prompt('\nSelect groups to assign/unassign (comma-separated numbers, or "clear" to remove all): ');
    
    if (selection.toLowerCase() === 'clear') {
      // Clear all group assignments
      ups.groups = [];
      logger.log('All group assignments cleared.');
      return;
    }
    
    if (!selection.trim()) {
      // No change if empty input
      return;
    }
    
    // Process selections
    const selections = selection.split(',').map(s => s.trim());
    
    for (const sel of selections) {
      const index = parseInt(sel, 10) - 1;
      if (isNaN(index) || index < 0 || index >= groups.length) {
        logger.error(`Invalid selection: ${sel}`);
        continue;
      }
      
      const group = groups[index];
      
      // Initialize groups array if needed (should already be done above)
      if (!ups.groups) {
        ups.groups = [];
      }
      
      // Toggle assignment
      const groupIndex = ups.groups.indexOf(group.id);
      if (groupIndex === -1) {
        // Add to group
        ups.groups.push(group.id);
        logger.log(`Added to group: ${group.name} (${group.id})`);
      } else {
        // Remove from group
        ups.groups.splice(groupIndex, 1);
        logger.log(`Removed from group: ${group.name} (${group.id})`);
      }
    }
  }
  
  /**
   * Assign UPS devices to a specific group
   * @param groupId Group ID to assign UPS devices to
   * @param config Full configuration
   * @param prompt Function to prompt for user input
   */
  public async assignUpsToGroup(
    groupId: string,
    config: any,
    prompt: (question: string) => Promise<string>
  ): Promise<void> {
    if (!config.upsDevices || config.upsDevices.length === 0) {
      logger.log('No UPS devices available. Use "nupst add" to add UPS devices.');
      return;
    }
    
    const group = config.groups.find(g => g.id === groupId);
    if (!group) {
      logger.error(`Group with ID "${groupId}" not found.`);
      return;
    }
    
    // Show current assignments
    logger.log(`\nUPS devices in group "${group.name}" (${group.id}):`);
    const upsInGroup = config.upsDevices.filter(ups => ups.groups && ups.groups.includes(groupId));
    if (upsInGroup.length === 0) {
      logger.log('- None');
    } else {
      for (const ups of upsInGroup) {
        logger.log(`- ${ups.name} (${ups.id})`);
      }
    }
    
    // Show all UPS devices
    logger.log('\nAvailable UPS devices:');
    for (let i = 0; i < config.upsDevices.length; i++) {
      const ups = config.upsDevices[i];
      const assigned = ups.groups && ups.groups.includes(groupId);
      logger.log(`${i + 1}) ${ups.name} (${ups.id}) [${assigned ? 'Assigned' : 'Not Assigned'}]`);
    }
    
    // Prompt for UPS selection
    const selection = await prompt('\nSelect UPS devices to assign/unassign (comma-separated numbers, or "clear" to remove all): ');
    
    if (selection.toLowerCase() === 'clear') {
      // Clear all UPS from this group
      for (const ups of config.upsDevices) {
        if (ups.groups) {
          const groupIndex = ups.groups.indexOf(groupId);
          if (groupIndex !== -1) {
            ups.groups.splice(groupIndex, 1);
          }
        }
      }
      logger.log(`All UPS devices removed from group "${group.name}".`);
      return;
    }
    
    if (!selection.trim()) {
      // No change if empty input
      return;
    }
    
    // Process selections
    const selections = selection.split(',').map(s => s.trim());
    
    for (const sel of selections) {
      const index = parseInt(sel, 10) - 1;
      if (isNaN(index) || index < 0 || index >= config.upsDevices.length) {
        logger.error(`Invalid selection: ${sel}`);
        continue;
      }
      
      const ups = config.upsDevices[index];
      
      // Initialize groups array if needed
      if (!ups.groups) {
        ups.groups = [];
      }
      
      // Toggle assignment
      const groupIndex = ups.groups.indexOf(groupId);
      if (groupIndex === -1) {
        // Add to group
        ups.groups.push(groupId);
        logger.log(`Added "${ups.name}" to group "${group.name}"`);
      } else {
        // Remove from group
        ups.groups.splice(groupIndex, 1);
        logger.log(`Removed "${ups.name}" from group "${group.name}"`);
      }
    }
  }
}