import path from 'path';
import type { FileSystem } from './file-operations';
import { validateFileExists, readFile } from './file-operations';
import type { DomainInfo } from '../templates/context';

/**
 * Pure function to capitalize first letter of a string
 */
export const capitalizeFirstLetter = (str: string): string => 
  str.charAt(0).toUpperCase() + str.slice(1);

/**
 * Pure function to create domain info from domain name
 */
export const createDomainInfo = (domainName: string): DomainInfo => ({
  name: domainName,
  capitalizedName: capitalizeFirstLetter(domainName)
});

/**
 * Pure function to build domains directory path
 */
export const buildDomainsPath = (projectRoot: string = process.cwd()): string =>
  path.join(projectRoot, 'src', 'domains');

/**
 * Pure function to build context directory path
 */
export const buildContextPath = (projectRoot: string = process.cwd()): string =>
  path.join(projectRoot, 'src', 'context');

/**
 * Pure function to build service context file path
 */
export const buildServiceContextFilePath = (projectRoot: string = process.cwd()): string =>
  path.join(buildContextPath(projectRoot), 'ServiceContext.tsx');

/**
 * Pure function to build service context test file path
 */
export const buildServiceContextTestFilePath = (projectRoot: string = process.cwd()): string =>
  path.join(buildContextPath(projectRoot), 'ServiceContext.test.tsx');

/**
 * Pure function to validate if a domain has required structure
 */
export const validateDomainStructure = async (
  fileSystem: FileSystem,
  domainPath: string,
  domainName: string
): Promise<boolean> => {
  const capitalizedName = capitalizeFirstLetter(domainName);
  
  const requiredFiles = [
    path.join(domainPath, 'services', `${capitalizedName}Service.ts`),
    path.join(domainPath, 'adapters', `${capitalizedName}Adapter.ts`)
  ];

  const validationResults = await Promise.all(
    requiredFiles.map(filePath => validateFileExists(fileSystem, filePath))
  );

  return validationResults.every(exists => exists);
};

/**
 * Pure function to filter valid domains from directory entries
 */
export const filterValidDomains = async (
  fileSystem: FileSystem,
  domainsPath: string,
  directoryEntries: string[]
): Promise<string[]> => {
  const validationPromises = directoryEntries.map(async (entry) => {
    const domainPath = path.join(domainsPath, entry);
    const isDirectory = await isDirectoryEntry(fileSystem, domainPath);
    
    if (!isDirectory) return null;
    
    const hasValidStructure = await validateDomainStructure(fileSystem, domainPath, entry);
    return hasValidStructure ? entry : null;
  });

  const validationResults = await Promise.all(validationPromises);
  return validationResults.filter((entry): entry is string => entry !== null);
};

/**
 * Pure function to check if path is a directory
 */
export const isDirectoryEntry = async (
  fileSystem: FileSystem,
  entryPath: string
): Promise<boolean> => {
  try {
    const stats = await fileSystem.stat(entryPath);
    return stats.isDirectory();
  } catch {
    return false;
  }
};

/**
 * Get all valid domains from the domains directory
 */
export const getExistingDomains = async (
  fileSystem: FileSystem,
  projectRoot: string = process.cwd()
): Promise<DomainInfo[]> => {
  const domainsPath = buildDomainsPath(projectRoot);
  
  const domainsExist = await validateFileExists(fileSystem, domainsPath);
  if (!domainsExist) {
    return [];
  }

  try {
    const entries = await fileSystem.readdir(domainsPath);
    const validDomainNames = await filterValidDomains(fileSystem, domainsPath, entries);
    
    return validDomainNames.map(createDomainInfo);
  } catch {
    return [];
  }
};

/**
 * Parse existing service context to extract current domains
 */
export const parseExistingServiceContext = async (
  fileSystem: FileSystem,
  projectRoot: string = process.cwd()
): Promise<DomainInfo[]> => {
  const contextFilePath = buildServiceContextFilePath(projectRoot);
  
  const contextExists = await validateFileExists(fileSystem, contextFilePath);
  if (!contextExists) {
    return [];
  }

  try {
    const content = await readFile(fileSystem, contextFilePath, 'utf8');
    const serviceMatches = content.match(/(\w+)Service:\s*\w+Service/g) || [];
    
    const domainNames = serviceMatches
      .map((match: string) => match.replace(/Service:\s*\w+Service/, ''))
      .filter((name: string) => name.length > 0);

    return domainNames.map(createDomainInfo);
  } catch {
    return [];
  }
};

/**
 * Find domains that are not yet in the service context
 */
export const findMissingDomains = async (
  fileSystem: FileSystem,
  projectRoot: string = process.cwd()
): Promise<DomainInfo[]> => {
  const [existingDomains, contextDomains] = await Promise.all([
    getExistingDomains(fileSystem, projectRoot),
    parseExistingServiceContext(fileSystem, projectRoot)
  ]);

  const contextDomainNames = new Set(contextDomains.map(d => d.name));
  
  return existingDomains.filter(domain => !contextDomainNames.has(domain.name));
}; 