import fs from 'fs-extra';
import path from 'path';
import readline from 'readline';
import type { FileSystem } from '../utils/file-operations';
import type { Logger } from '../utils/logging';
import { 
  logError, 
  logSuccess, 
  logInfo,
  logWarning 
} from '../utils/logging';
import {
  getExistingDomains
} from '../utils/domain-analyzer';
import {
  getMswSetupTemplate,
  getMswBrowserTemplate,
  getMswServerTemplate,
  getMswHandlersTemplate,
  getMswHandlersIndexTemplate,
  getMswMocksTemplate,
  getMswSetupTestsTemplate
} from '../templates/msw';
import { analyzeDomain } from '../utils/domain-file-analyzer';
import { generateDynamicMswHandlers, generateDynamicMockData } from '../templates/msw-dynamic';

/**
 * Pure function to validate MSW init preconditions
 */
const validateMswInitPreconditions = async (
  fileSystem: FileSystem,
  mswSetupPath: string
): Promise<{ canInit: boolean; reason?: string }> => {
  const mswExists = await fileSystem.exists(mswSetupPath);
  
  if (mswExists) {
    return {
      canInit: false,
      reason: 'MSW is already initialized in this project'
    };
  }
  
  return { canInit: true };
};

/**
 * Pure function to ask for user confirmation
 */
const askForConfirmation = (question: string): Promise<boolean> => {
  return new Promise((resolve) => {
    const rl = readline.createInterface({
      input: process.stdin,
      output: process.stdout
    });

    rl.question(`${question} (y/N): `, (answer) => {
      rl.close();
      resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
    });
  });
};

/**
 * Create MSW directory structure and configuration files
 */
const createMswFiles = async (
  fileSystem: FileSystem,
  mswPath: string,
  setupFilePath: string,
  browserFilePath: string,
  serverFilePath: string,
  setupTestsFilePath: string,
  setupContent: string,
  browserContent: string,
  serverContent: string,
  setupTestsContent: string
): Promise<void> => {
  await fileSystem.ensureDir(mswPath);
  await fileSystem.ensureDir(path.join(mswPath, 'handlers'));
  await fileSystem.writeFile(setupFilePath, setupContent, 'utf8');
  await fileSystem.writeFile(browserFilePath, browserContent, 'utf8');
  await fileSystem.writeFile(serverFilePath, serverContent, 'utf8');
  await fileSystem.writeFile(setupTestsFilePath, setupTestsContent, 'utf8');
};

/**
 * Initialize MSW configuration in the project
 */
export const initMsw = async (
  fileSystem: FileSystem = fs,
  log: Logger = require('../utils/logger').logger,
  projectRoot: string = process.cwd()
): Promise<void> => {
  const srcPath = path.join(projectRoot, 'src');
  const mswPath = path.join(srcPath, 'mocks');
  const setupFilePath = path.join(mswPath, 'setup.ts');
  const browserFilePath = path.join(mswPath, 'browser.ts');
  const serverFilePath = path.join(mswPath, 'server.ts');
  const setupTestsFilePath = path.join(srcPath, 'setupTests.ts');

  try {
    const validation = await validateMswInitPreconditions(fileSystem, setupFilePath);
    
    if (!validation.canInit) {
      logWarning(log, validation.reason || 'Cannot initialize MSW');
      return;
    }

    logInfo(log, 'Initializing MSW (Mock Service Worker) configuration...');

    const setupContent = getMswSetupTemplate();
    const browserContent = getMswBrowserTemplate();
    const serverContent = getMswServerTemplate();
    const setupTestsContent = getMswSetupTestsTemplate();
    
    // Create empty handlers index initially
    const handlersIndexPath = path.join(mswPath, 'handlers', 'index.ts');
    const emptyHandlersIndex = getMswHandlersIndexTemplate([]);

    await createMswFiles(
      fileSystem,
      mswPath,
      setupFilePath,
      browserFilePath,
      serverFilePath,
      setupTestsFilePath,
      setupContent,
      browserContent,
      serverContent,
      setupTestsContent
    );
    
    // Create the handlers index file
    await fileSystem.writeFile(handlersIndexPath, emptyHandlersIndex, 'utf8');

    logSuccess(log, 'MSW initialized successfully!');
    logInfo(log, `📁 MSW configuration created at: ${mswPath}`);
    logInfo(log, '📦 Install MSW with: bun add -D msw');
    logInfo(log, '🔧 Generate browser worker: bunx msw init public/ --save');
    logInfo(log, '💡 Use "ruch msw handlers generate" to create domain handlers');
  } catch (error) {
    logError(log, `Error initializing MSW: ${error instanceof Error ? error.message : 'Unknown error'}`);
  }
};

/**
 * Generate MSW handlers for all domains
 */
export const generateMswHandlers = async (
  fileSystem: FileSystem = fs,
  log: Logger = require('../utils/logger').logger,
  projectRoot: string = process.cwd()
): Promise<void> => {
  const srcPath = path.join(projectRoot, 'src');
  const mswPath = path.join(srcPath, 'mocks');
  const handlersPath = path.join(mswPath, 'handlers');

  try {
    const mswExists = await fileSystem.exists(mswPath);
    
    if (!mswExists) {
      logError(log, 'MSW not initialized. Run "ruch msw init" first.');
      return;
    }

    logInfo(log, 'Generating MSW handlers for all domains...');

    const domains = await getExistingDomains(fileSystem, projectRoot);
    
    if (domains.length === 0) {
      logWarning(log, 'No domains found. Create domains first with "ruch create <domain-name>".');
      return;
    }

    let handlersGenerated = 0;

    for (const domain of domains) {
      const handlerFilePath = path.join(handlersPath, `${domain.name}.ts`);
      const mocksFolderPath = path.join(srcPath, 'domains', domain.name, 'mocks');
      
      // Check if handler already exists
      const handlerExists = await fileSystem.exists(handlerFilePath);
      
      if (!handlerExists) {
        const handlerContent = getMswHandlersTemplate(domain.name);
        await fileSystem.writeFile(handlerFilePath, handlerContent, 'utf8');
        handlersGenerated++;
        
        // Create domain mocks folder
        await fileSystem.ensureDir(mocksFolderPath);
        
        logInfo(log, `✅ Created handler: ${domain.name}.ts`);
      } else {
        logInfo(log, `⏭️  Handler already exists: ${domain.name}.ts`);
      }
    }

    // Update setup files to include all handlers
    const setupFilePath = path.join(mswPath, 'setup.ts');
    const browserFilePath = path.join(mswPath, 'browser.ts');
    const serverFilePath = path.join(mswPath, 'server.ts');
    const handlersIndexPath = path.join(handlersPath, 'index.ts');

    const domainNames = domains.map(d => d.name);
    const setupContent = getMswSetupTemplate();
    const browserContent = getMswBrowserTemplate();
    const serverContent = getMswServerTemplate();
    const handlersIndexContent = getMswHandlersIndexTemplate(domainNames);

    await fileSystem.writeFile(setupFilePath, setupContent, 'utf8');
    await fileSystem.writeFile(browserFilePath, browserContent, 'utf8');
    await fileSystem.writeFile(serverFilePath, serverContent, 'utf8');
    await fileSystem.writeFile(handlersIndexPath, handlersIndexContent, 'utf8');

    logSuccess(log, `MSW handlers generated successfully!`);
    logInfo(log, `📁 Generated ${handlersGenerated} new handler(s)`);
    logInfo(log, `📁 Updated setup files with ${domains.length} domain(s)`);
    logInfo(log, '💡 Use "ruch msw mocks generate" to create mock data');
  } catch (error) {
    logError(log, `Error generating MSW handlers: ${error instanceof Error ? error.message : 'Unknown error'}`);
  }
};

/**
 * Generate mock data for all domains
 */
export const generateMswMocks = async (
  fileSystem: FileSystem = fs,
  log: Logger = require('../utils/logger').logger,
  projectRoot: string = process.cwd()
): Promise<void> => {
  const srcPath = path.join(projectRoot, 'src');

  try {
    const mswPath = path.join(srcPath, 'mocks');
    const mswExists = await fileSystem.exists(mswPath);
    
    if (!mswExists) {
      logError(log, 'MSW not initialized. Run "ruch msw init" first.');
      return;
    }

    logInfo(log, 'Generating mock data for all domains...');

    const domains = await getExistingDomains(fileSystem, projectRoot);
    
    if (domains.length === 0) {
      logWarning(log, 'No domains found. Create domains first with "ruch create <domain-name>".');
      return;
    }

    let mocksGenerated = 0;

    for (const domain of domains) {
      const domainMocksPath = path.join(srcPath, 'domains', domain.name, 'mocks');
      const mockDataFilePath = path.join(domainMocksPath, 'mockData.ts');
      
      // Check if mock data already exists
      const mockDataExists = await fileSystem.exists(mockDataFilePath);
      
      if (!mockDataExists) {
        await fileSystem.ensureDir(domainMocksPath);
        const mockContent = getMswMocksTemplate(domain.name);
        await fileSystem.writeFile(mockDataFilePath, mockContent, 'utf8');
        mocksGenerated++;
        
        logInfo(log, `✅ Created mock data: ${domain.name}/mocks/mockData.ts`);
      } else {
        logInfo(log, `⏭️  Mock data already exists: ${domain.name}/mocks/mockData.ts`);
      }
    }

    logSuccess(log, `Mock data generated successfully!`);
    logInfo(log, `📁 Generated ${mocksGenerated} new mock file(s)`);
    logInfo(log, '💡 Edit mock data files to match your domain entities');
    logInfo(log, '💡 Use "ruch msw update" to regenerate after schema changes');
  } catch (error) {
    logError(log, `Error generating mock data: ${error instanceof Error ? error.message : 'Unknown error'}`);
  }
};

/**
 * Update MSW configuration and handlers for a specific domain
 */
export const updateMswDomain = async (
  domainName: string,
  fileSystem: FileSystem = fs,
  log: Logger = require('../utils/logger').logger,
  projectRoot: string = process.cwd(),
  skipConfirmation: boolean = false
): Promise<void> => {
  const srcPath = path.join(projectRoot, 'src');
  const mswPath = path.join(srcPath, 'mocks');
  const handlersPath = path.join(mswPath, 'handlers');

  try {
    const mswExists = await fileSystem.exists(mswPath);
    
    if (!mswExists) {
      logError(log, 'MSW not initialized. Run "ruch msw init" first.');
      return;
    }

    // Check if domain exists
    const domains = await getExistingDomains(fileSystem, projectRoot);
    const targetDomain = domains.find(d => d.name === domainName);
    
    if (!targetDomain) {
      logError(log, `Domain "${domainName}" not found. Available domains: ${domains.map(d => d.name).join(', ')}`);
      return;
    }

    logInfo(log, `Updating MSW configuration for domain "${domainName}"...`);

    // Create backups of the specific handler and related files
    const handlerFilePath = path.join(handlersPath, `${domainName}.ts`);
    const domainMocksPath = path.join(srcPath, 'domains', domainName, 'mocks', 'mockData.ts');
    const handlersIndexPath = path.join(handlersPath, 'index.ts');
    
    const timestamp = Date.now();
    
    // Backup handler if it exists
    if (await fileSystem.exists(handlerFilePath)) {
      const backupHandlerPath = `${handlerFilePath}.backup.${timestamp}`;
      const handlerContent = await fileSystem.readFile(handlerFilePath, 'utf8');
      await fileSystem.writeFile(backupHandlerPath, handlerContent, 'utf8');
      logInfo(log, `📁 Handler backup: ${path.basename(backupHandlerPath)}`);
    }
    
    // Backup mock data if it exists
    if (await fileSystem.exists(domainMocksPath)) {
      const backupMockPath = `${domainMocksPath}.backup.${timestamp}`;
      const mockContent = await fileSystem.readFile(domainMocksPath, 'utf8');
      await fileSystem.writeFile(backupMockPath, mockContent, 'utf8');
      logInfo(log, `📁 Mock data backup: ${path.basename(backupMockPath)}`);
    }
    
    logWarning(log, `⚠️  WARNING: This will regenerate MSW files for domain "${domainName}"`);
    logInfo(log, `📁 Backups created with timestamp: ${timestamp}`);
    logInfo(log, '💡 This will overwrite handler and mock data files');

    // Ask for confirmation unless skipped (for tests)
    if (!skipConfirmation) {
      const confirmed = await askForConfirmation('Do you want to continue?');
      if (!confirmed) {
        logInfo(log, '❌ Operation cancelled by user');
        return;
      }
    }

    // Analyze the domain to get real structure
    const domainPath = path.join(srcPath, 'domains', domainName);
    logInfo(log, `🔍 Analyzing domain structure...`);
    
    try {
      const analysis = await analyzeDomain(domainPath, domainName);
      
      logInfo(log, `📊 Found ${analysis.entities.length} entities, ${analysis.adapters.length} adapters, ${analysis.ports.length} ports`);
      
      // Generate dynamic handler based on real domain structure
      const handlerContent = generateDynamicMswHandlers(analysis);
      await fileSystem.writeFile(handlerFilePath, handlerContent, 'utf8');
      
      // Ensure domain mocks folder exists
      const domainMocksFolderPath = path.join(srcPath, 'domains', domainName, 'mocks');
      await fileSystem.ensureDir(domainMocksFolderPath);
      
      // Generate dynamic mock data based on real entities
      const mockContent = generateDynamicMockData(analysis);
      await fileSystem.writeFile(domainMocksPath, mockContent, 'utf8');
      
      // Log what was found and generated
      if (analysis.entities.length > 0) {
        logInfo(log, `📝 Entities analyzed: ${analysis.entities.map(e => e.name).join(', ')}`);
      }
      if (analysis.adapters.length > 0) {
        logInfo(log, `🔌 Adapters analyzed: ${analysis.adapters.map(a => a.name).join(', ')}`);
      }
      if (analysis.ports.length > 0) {
        const totalMethods = analysis.ports.reduce((sum, p) => sum + p.methods.length, 0);
        logInfo(log, `⚙️  Generated ${totalMethods} API endpoint(s) based on port methods`);
      }
      
      // Important note about customization needed
      logWarning(log, '⚠️  IMPORTANT: MSW handlers are based on your Port interfaces.');
      logInfo(log, '📝 You must customize the endpoints to match your real API:');
      logInfo(log, '   • Update API_BASE to your actual base URL');
      logInfo(log, '   • Modify endpoint paths for each method');
      logInfo(log, '   • Customize request/response handling logic');
      logWarning(log, '⚠️  IMPORTANT: Mock data is generated from your Entity definitions.');
      logInfo(log, '📝 You should customize the mock data to match your business needs:');
      logInfo(log, '   • Update sample data with realistic values');
      logInfo(log, '   • Add domain-specific business logic');
      logInfo(log, '   • Customize validation and error scenarios');
      
    } catch (error) {
      logWarning(log, `⚠️  Could not analyze domain structure: ${error instanceof Error ? error.message : 'Unknown error'}`);
      logInfo(log, '📄 Falling back to generic templates...');
      
      // Fallback to generic templates
      const handlerContent = getMswHandlersTemplate(domainName);
      await fileSystem.writeFile(handlerFilePath, handlerContent, 'utf8');
      
      const domainMocksFolderPath = path.join(srcPath, 'domains', domainName, 'mocks');
      await fileSystem.ensureDir(domainMocksFolderPath);
      
      const mockContent = getMswMocksTemplate(domainName);
      await fileSystem.writeFile(domainMocksPath, mockContent, 'utf8');
    }
    
    // Update handlers index to ensure it includes all domains
    const allDomains = await getExistingDomains(fileSystem, projectRoot);
    const domainNames = allDomains.map(d => d.name);
    const handlersIndexContent = getMswHandlersIndexTemplate(domainNames);
    await fileSystem.writeFile(handlersIndexPath, handlersIndexContent, 'utf8');

    logSuccess(log, `MSW configuration updated for domain "${domainName}"!`);
    logInfo(log, `✅ Regenerated handler: ${domainName}.ts`);
    logInfo(log, `✅ Regenerated mock data: ${domainName}/mocks/mockData.ts`);
    logWarning(log, `🔄 IMPORTANT: If you had custom modifications, they were overwritten!`);
    logInfo(log, `📁 Backup files created with timestamp: ${timestamp}`);
    logInfo(log, `💡 To restore your custom changes:`);
    logInfo(log, `   • Check .backup.${timestamp} files in handlers/ and domain/mocks/`);
    logInfo(log, `   • Copy your custom endpoint paths and logic back to the new files`);
    logInfo(log, `   • Merge your custom mock data with the newly generated structure`);
  } catch (error) {
    logError(log, `Error updating MSW for domain "${domainName}": ${error instanceof Error ? error.message : 'Unknown error'}`);
  }
};

/**
 * Update MSW configuration and handlers
 */
export const updateMsw = async (
  fileSystem: FileSystem = fs,
  log: Logger = require('../utils/logger').logger,
  projectRoot: string = process.cwd(),
  skipConfirmation: boolean = false
): Promise<void> => {
  const srcPath = path.join(projectRoot, 'src');
  const mswPath = path.join(srcPath, 'mocks');

  try {
    const mswExists = await fileSystem.exists(mswPath);
    
    if (!mswExists) {
      logError(log, 'MSW not initialized. Run "ruch msw init" first.');
      return;
    }

    logInfo(log, 'Checking for MSW updates...');

    const domains = await getExistingDomains(fileSystem, projectRoot);
    const handlersPath = path.join(mswPath, 'handlers');
    
    // Check for missing handlers
    const missingHandlers = [];
    for (const domain of domains) {
      const handlerFilePath = path.join(handlersPath, `${domain.name}.ts`);
      const handlerExists = await fileSystem.exists(handlerFilePath);
      if (!handlerExists) {
        missingHandlers.push(domain.name);
      }
    }

    if (missingHandlers.length === 0) {
      logInfo(log, 'MSW configuration is already up to date. No missing handlers found.');
      return;
    }

    logInfo(log, `Found ${missingHandlers.length} missing handler(s):`);
    missingHandlers.forEach(handler => logInfo(log, `  - ${handler}`));

    // Create backup of setup files
    const setupFilePath = path.join(mswPath, 'setup.ts');
    const browserFilePath = path.join(mswPath, 'browser.ts');
    const serverFilePath = path.join(mswPath, 'server.ts');
    
    const timestamp = Date.now();
    const backupSetupPath = `${setupFilePath}.backup.${timestamp}`;
    const backupBrowserPath = `${browserFilePath}.backup.${timestamp}`;
    const backupServerPath = `${serverFilePath}.backup.${timestamp}`;
    
    if (await fileSystem.exists(setupFilePath)) {
      const setupContent = await fileSystem.readFile(setupFilePath, 'utf8');
      await fileSystem.writeFile(backupSetupPath, setupContent, 'utf8');
    }
    
    if (await fileSystem.exists(browserFilePath)) {
      const browserContent = await fileSystem.readFile(browserFilePath, 'utf8');
      await fileSystem.writeFile(backupBrowserPath, browserContent, 'utf8');
    }
    
    if (await fileSystem.exists(serverFilePath)) {
      const serverContent = await fileSystem.readFile(serverFilePath, 'utf8');
      await fileSystem.writeFile(backupServerPath, serverContent, 'utf8');
    }
    
    logWarning(log, '⚠️  WARNING: This will update your MSW configuration files');
    logInfo(log, `📁 Backups created with timestamp: ${timestamp}`);
    logInfo(log, '💡 Review the backups if you had custom modifications');

    // Ask for confirmation unless skipped (for tests)
    if (!skipConfirmation) {
      const confirmed = await askForConfirmation('Do you want to continue?');
      if (!confirmed) {
        logInfo(log, '❌ Operation cancelled by user');
        // Remove backups since we're not proceeding
        await fileSystem.remove(backupSetupPath).catch(() => {});
        await fileSystem.remove(backupBrowserPath).catch(() => {});
        await fileSystem.remove(backupServerPath).catch(() => {});
        return;
      }
    }

    // Generate missing handlers
    for (const handlerName of missingHandlers) {
      const handlerFilePath = path.join(handlersPath, `${handlerName}.ts`);
      const handlerContent = getMswHandlersTemplate(handlerName);
      await fileSystem.writeFile(handlerFilePath, handlerContent, 'utf8');
      
      // Create domain mocks folder if it doesn't exist
      const domainMocksPath = path.join(srcPath, 'domains', handlerName, 'mocks');
      await fileSystem.ensureDir(domainMocksPath);
    }

    // Update setup files
    const handlersIndexPath = path.join(handlersPath, 'index.ts');
    const domainNames = domains.map(d => d.name);
    
    const setupContent = getMswSetupTemplate();
    const browserContent = getMswBrowserTemplate();
    const serverContent = getMswServerTemplate();
    const handlersIndexContent = getMswHandlersIndexTemplate(domainNames);

    await fileSystem.writeFile(setupFilePath, setupContent, 'utf8');
    await fileSystem.writeFile(browserFilePath, browserContent, 'utf8');
    await fileSystem.writeFile(serverFilePath, serverContent, 'utf8');
    await fileSystem.writeFile(handlersIndexPath, handlersIndexContent, 'utf8');

    logSuccess(log, 'MSW configuration updated successfully!');
    logInfo(log, `Added ${missingHandlers.length} handler(s) to MSW configuration`);
    logInfo(log, `Total domains: ${domains.length}`);
    logWarning(log, `🔄 IMPORTANT: If you had custom modifications, they were overwritten!`);
    logInfo(log, `📁 Backup files created with timestamp: ${timestamp}`);
    logInfo(log, `💡 To restore your custom changes:`);
    logInfo(log, `   • Check .backup.${timestamp} files in mocks/ folder`);
    logInfo(log, `   • Copy your custom configuration back to the new setup files`);
  } catch (error) {
    logError(log, `Error updating MSW: ${error instanceof Error ? error.message : 'Unknown error'}`);
  }
}; 