import fs from 'fs-extra';
import path from 'path';
import { logger } from '../utils/logger';
import { getDomainsSourceDirectoryPath } from '../utils/paths';
import type { FileSystem } from '../utils/file-operations';
import { 
  validateFileExists, 
  readDirectory, 
  getFileMetadata 
} from '../utils/file-operations';
import type { Logger } from '../utils/logging';
import { 
  logError, 
  logNoDomainsFound, 
  logDomainsListed 
} from '../utils/logging';

// Types
interface DomainStructure {
  name: string;
  path: string;
  folders: string[];
}

interface DomainDisplay {
  icon: string;
  name: string;
  folders: Array<{
    icon: string;
    name: string;
  }>;
}

// Pure functions
const buildDomainDirectoryPath = (basePath: string, domainName: string): string => 
  path.join(basePath, domainName);

const isDirectory = (stats: fs.Stats): boolean => 
  stats.isDirectory();

const sortDomainFolders = (folders: string[]): string[] => {
  const folderOrder = ['entities', 'ports', 'services', 'adapters', 'hooks', 'ui'];
  return folders.sort((a, b) => {
    const aIndex = folderOrder.indexOf(a);
    const bIndex = folderOrder.indexOf(b);
    
    if (aIndex === -1 && bIndex === -1) return a.localeCompare(b);
    if (aIndex === -1) return 1;
    if (bIndex === -1) return -1;
    return aIndex - bIndex;
  });
};

const getDomainDisplayIcon = (index: number, total: number): string => 
  index === total - 1 ? '└──' : '├──';

const getFolderDisplayIcon = (folderIndex: number, totalFolders: number, isLastDomain: boolean): string => {
  if (folderIndex === totalFolders - 1 && isLastDomain) return '    └──';
  if (folderIndex === totalFolders - 1) return '│   └──';
  if (isLastDomain) return '    ├──';
  return '│   ├──';
};

// Domain operations
const getDomainFolderStructure = async (
  fileSystem: FileSystem,
  domainPath: string
): Promise<string[]> => {
  const directoryEntries = await fileSystem.readdir(domainPath);
  const domainFolders: string[] = [];
  
  for (const entry of directoryEntries) {
    const entryPath = path.join(domainPath, entry);
    const stats = await fileSystem.stat(entryPath);
    if (isDirectory(stats)) {
      domainFolders.push(entry);
    }
  }
  
  return sortDomainFolders(domainFolders);
};

const analyzeDomainStructures = async (
  fileSystem: FileSystem,
  basePath: string,
  entries: string[]
): Promise<DomainStructure[]> => {
  const domainStructures: DomainStructure[] = [];
  
  for (const entry of entries) {
    const entryPath = buildDomainDirectoryPath(basePath, entry);
    const stats = await fileSystem.stat(entryPath);
    if (isDirectory(stats)) {
      const folders = await getDomainFolderStructure(fileSystem, entryPath);
      domainStructures.push({ name: entry, path: entryPath, folders });
    }
  }
  
  return domainStructures;
};

// Display operations
const formatDomainDisplay = (
  domain: DomainStructure,
  index: number,
  total: number
): DomainDisplay => {
  const icon = getDomainDisplayIcon(index, total);
  const folders = domain.folders.map((folder, folderIndex) => ({
    icon: getFolderDisplayIcon(folderIndex, domain.folders.length, index === total - 1),
    name: folder
  }));
  
  return { icon, name: domain.name, folders };
};

const displayDomainStructure = (log: Logger, display: DomainDisplay): void => {
  log.log(`${display.icon} 📁 ${display.name}`);
  display.folders.forEach(folder => {
    log.log(`${folder.icon} 📂 ${folder.name}`);
  });
};

const displayDomainSeparator = (log: Logger, index: number, total: number): void => {
  if (index < total - 1) {
    log.log('│');
  }
};

// Main function
export const listDomains = async (
  fileSystem: FileSystem = fs,
  log: Logger = logger
): Promise<void> => {
  const domainsSourcePath = getDomainsSourceDirectoryPath();

  try {
    if (!(await fileSystem.exists(domainsSourcePath))) {
      logNoDomainsFound(log, domainsSourcePath);
      return;
    }

    const directoryEntries = await fileSystem.readdir(domainsSourcePath);
    const domainStructures = await analyzeDomainStructures(fileSystem, domainsSourcePath, directoryEntries);

    if (domainStructures.length === 0) {
      logNoDomainsFound(log, domainsSourcePath);
      return;
    }

    logDomainsListed(log, domainStructures.length, domainsSourcePath);
    log.log('');

    domainStructures.forEach((domain, index) => {
      const display = formatDomainDisplay(domain, index, domainStructures.length);
      displayDomainStructure(log, display);
      displayDomainSeparator(log, index, domainStructures.length);
    });

    log.log('');
  } catch (error: unknown) {
    logError(log, `Error reading domains: ${error instanceof Error ? error.message : 'Unknown error'}`);
  }
}; 