import fs from 'fs-extra';
import path from 'path';
import readline from 'readline';
import type { FileSystem } from '../utils/file-operations';
import { 
  validateFileExists, 
  createDirectory, 
  writeFile,
  readFile
} from '../utils/file-operations';
import type { Logger } from '../utils/logging';
import { 
  logError, 
  logSuccess, 
  logInfo,
  logWarning 
} from '../utils/logging';
import {
  buildContextPath,
  buildServiceContextFilePath,
  buildServiceContextTestFilePath,
  getExistingDomains,
  parseExistingServiceContext,
  findMissingDomains
} from '../utils/domain-analyzer';
import {
  getEmptyServiceContextTemplate,
  getServiceContextTemplate,
  getServiceContextTestTemplate
} from '../templates/context';
import type { DomainInfo } from '../templates/context';

/**
 * Pure function to validate context creation preconditions
 */
const validateContextCreationPreconditions = async (
  fileSystem: FileSystem,
  contextFilePath: string
): Promise<{ canCreate: boolean; reason?: string }> => {
  const contextExists = await fileSystem.exists(contextFilePath);
  
  if (contextExists) {
    return {
      canCreate: false,
      reason: 'Service context already exists'
    };
  }
  
  return { canCreate: true };
};

/**
 * Pure function to validate context update preconditions
 */
const validateContextUpdatePreconditions = async (
  fileSystem: FileSystem,
  contextFilePath: string
): Promise<{ canUpdate: boolean; reason?: string }> => {
  const contextExists = await fileSystem.exists(contextFilePath);
  
  if (!contextExists) {
    return {
      canUpdate: false,
      reason: 'Service context does not exist. Use "context init" or "context generate" first.'
    };
  }
  
  return { canUpdate: true };
};

/**
 * Pure function to create context files
 */
const createContextFiles = async (
  fileSystem: FileSystem,
  contextPath: string,
  contextFilePath: string,
  testFilePath: string,
  contextContent: string,
  testContent: string
): Promise<void> => {
  await fileSystem.ensureDir(contextPath);
  await fileSystem.writeFile(contextFilePath, contextContent, 'utf8');
  await fileSystem.writeFile(testFilePath, testContent, 'utf8');
};

/**
 * Pure function to log context creation success
 */
const logContextCreationSuccess = (
  log: Logger,
  contextFilePath: string,
  domains: DomainInfo[]
): void => {
  logSuccess(log, 'Service context created successfully!');
  logInfo(log, `Location: ${contextFilePath}`);
  
  if (domains.length > 0) {
    logInfo(log, `Integrated ${domains.length} domain(s):`);
    domains.forEach(domain => logInfo(log, `  - ${domain.name}`));
  }
  
  logInfo(log, '💡 Import with: import { useServices } from \'./context/ServiceContext\'');
};

/**
 * 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');
    });
  });
};

/**
 * Initialize empty service context
 */
export const initServiceContext = async (
  fileSystem: FileSystem = fs,
  log: Logger = require('../utils/logger').logger,
  projectRoot: string = process.cwd()
): Promise<void> => {
  const contextPath = buildContextPath(projectRoot);
  const contextFilePath = buildServiceContextFilePath(projectRoot);
  const testFilePath = buildServiceContextTestFilePath(projectRoot);

  try {
    const validation = await validateContextCreationPreconditions(fileSystem, contextFilePath);
    
    if (!validation.canCreate) {
      logWarning(log, validation.reason || 'Cannot create service context');
      return;
    }

    logInfo(log, 'Initializing empty service context...');

    const contextContent = getEmptyServiceContextTemplate();
    const testContent = getServiceContextTestTemplate([]);

    await createContextFiles(
      fileSystem,
      contextPath,
      contextFilePath,
      testFilePath,
      contextContent,
      testContent
    );

    logContextCreationSuccess(log, contextFilePath, []);
  } catch (error) {
    logError(log, `Error initializing service context: ${error instanceof Error ? error.message : 'Unknown error'}`);
  }
};

/**
 * Generate service context with all existing domains
 */
export const generateServiceContext = async (
  fileSystem: FileSystem = fs,
  log: Logger = require('../utils/logger').logger,
  projectRoot: string = process.cwd()
): Promise<void> => {
  const contextPath = buildContextPath(projectRoot);
  const contextFilePath = buildServiceContextFilePath(projectRoot);
  const testFilePath = buildServiceContextTestFilePath(projectRoot);

  try {
    const contextExists = await fileSystem.exists(contextFilePath);
    
    if (contextExists) {
      logWarning(log, 'Service context already exists. Use "context update" to add new domains.');
      return;
    }

    logInfo(log, 'Generating service context with existing domains...');

    const domains = await getExistingDomains(fileSystem, projectRoot);
    
    if (domains.length === 0) {
      logWarning(log, 'No domains found. Creating empty service context.');
      await initServiceContext(fileSystem, log, projectRoot);
      return;
    }

    const contextContent = getServiceContextTemplate(domains);
    const testContent = getServiceContextTestTemplate(domains);

    await createContextFiles(
      fileSystem,
      contextPath,
      contextFilePath,
      testFilePath,
      contextContent,
      testContent
    );

    logContextCreationSuccess(log, contextFilePath, domains);
  } catch (error) {
    logError(log, `Error generating service context: ${error instanceof Error ? error.message : 'Unknown error'}`);
  }
};

/**
 * Update service context with missing domains
 */
export const updateServiceContext = async (
  fileSystem: FileSystem = fs,
  log: Logger = require('../utils/logger').logger,
  projectRoot: string = process.cwd(),
  skipConfirmation: boolean = false
): Promise<void> => {
  const contextFilePath = buildServiceContextFilePath(projectRoot);
  const testFilePath = buildServiceContextTestFilePath(projectRoot);

  try {
    const validation = await validateContextUpdatePreconditions(fileSystem, contextFilePath);
    
    if (!validation.canUpdate) {
      logError(log, validation.reason || 'Cannot update service context');
      return;
    }

    logInfo(log, 'Checking for missing domains...');

    const missingDomains = await findMissingDomains(fileSystem, projectRoot);
    
    if (missingDomains.length === 0) {
      logInfo(log, 'Service context is already up to date. No missing domains found.');
      return;
    }

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

    // Create backup before overwriting
    const backupPath = `${contextFilePath}.backup.${Date.now()}`;
    const existingContent = await fileSystem.readFile(contextFilePath, 'utf8');
    await fileSystem.writeFile(backupPath, existingContent, 'utf8');
    
    logWarning(log, '⚠️  WARNING: This will overwrite your existing ServiceContext.tsx');
    logInfo(log, `📁 Backup created at: ${backupPath}`);
    logInfo(log, '💡 Review the backup 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 the backup since we're not proceeding
        await fileSystem.remove(backupPath);
        return;
      }
    }

    // Get all domains (existing + missing) for regeneration
    const allDomains = await getExistingDomains(fileSystem, projectRoot);
    
    const contextContent = getServiceContextTemplate(allDomains);
    const testContent = getServiceContextTestTemplate(allDomains);

    await fileSystem.writeFile(contextFilePath, contextContent, 'utf8');
    await fileSystem.writeFile(testFilePath, testContent, 'utf8');

    logSuccess(log, 'Service context updated successfully!');
    logInfo(log, `Added ${missingDomains.length} domain(s) to service context`);
    logInfo(log, `Total domains: ${allDomains.length}`);
    logInfo(log, `🔄 If you had custom code, restore it from: ${backupPath}`);
  } catch (error) {
    logError(log, `Error updating service context: ${error instanceof Error ? error.message : 'Unknown error'}`);
  }
}; 