import { readFileSync, existsSync } from 'fs';
import { resolve, join } from 'path';
import type { CoverageConfig, CLIOptions } from './types';

const DEFAULT_CONFIG: CoverageConfig = {
  exclude: [
    '**/node_modules/**',
    '**/dist/**',
    '**/build/**',
    '**/coverage/**',
    '**/*.test.ts',
    '**/*.test.js',
    '**/*.spec.ts',
    '**/*.spec.js',
    '**/test/**',
    '**/tests/**',
    '**/__tests__/**',
    '**/__mocks__/**',
    '**/setup.ts',
    '**/setup.js',
  ],
  reporters: ['terminal'],
  thresholds: {
    lines: 80,
    functions: 80,
    branches: 80,
    statements: 80,
  },
  outputDir: 'coverage',
};

export function loadConfig(options: CLIOptions): CoverageConfig {
  let config = { ...DEFAULT_CONFIG };

  // Load from config file
  const configPath = options.config || findConfigFile();
  if (configPath && existsSync(configPath)) {
    try {
      const fileConfig = JSON.parse(readFileSync(configPath, 'utf-8'));
      config = mergeConfig(config, fileConfig);
    } catch (error) {
      console.warn(`Warning: Failed to load config from ${configPath}:`, error);
    }
  }

  // Override with CLI options
  if (options.exclude && options.exclude.length > 0) {
    config.exclude = options.exclude;
  }

  if (options.include && options.include.length > 0) {
    config.include = options.include;
  }

  if (options.reporters && options.reporters.length > 0) {
    config.reporters = options.reporters as any[];
  }

  if (options.outputDir) {
    config.outputDir = options.outputDir;
  }

  // Handle thresholds
  if (options.threshold !== undefined) {
    config.thresholds = {
      lines: options.threshold,
      functions: options.threshold,
      branches: options.threshold,
      statements: options.threshold,
    };
  }

  if (options.thresholdLines !== undefined) {
    config.thresholds.lines = options.thresholdLines;
  }
  if (options.thresholdFunctions !== undefined) {
    config.thresholds.functions = options.thresholdFunctions;
  }
  if (options.thresholdBranches !== undefined) {
    config.thresholds.branches = options.thresholdBranches;
  }
  if (options.thresholdStatements !== undefined) {
    config.thresholds.statements = options.thresholdStatements;
  }

  if (options.packages) {
    config.packages = options.packages;
  }

  // Find workspace root
  config.workspaceRoot = findWorkspaceRoot();

  return config;
}

function findConfigFile(): string | null {
  const configNames = ['.coveragerc.json', '.coveragerc', 'coverage.config.json'];
  const searchDirs = [process.cwd(), ...getParentDirs(process.cwd())];

  for (const dir of searchDirs) {
    for (const name of configNames) {
      const path = join(dir, name);
      if (existsSync(path)) {
        return path;
      }
    }
  }

  return null;
}

function findWorkspaceRoot(): string {
  const searchDirs = [process.cwd(), ...getParentDirs(process.cwd())];

  for (const dir of searchDirs) {
    // Check for common workspace indicators
    if (
      existsSync(join(dir, 'package.json')) &&
      (existsSync(join(dir, 'packages')) ||
        existsSync(join(dir, 'apps')) ||
        existsSync(join(dir, 'libs')))
    ) {
      return dir;
    }
  }

  return process.cwd();
}

function getParentDirs(dir: string): string[] {
  const parents: string[] = [];
  let current = resolve(dir);
  let parent = resolve(current, '..');

  while (parent !== current) {
    parents.push(parent);
    current = parent;
    parent = resolve(current, '..');
  }

  return parents;
}

function mergeConfig(base: CoverageConfig, override: Partial<CoverageConfig>): CoverageConfig {
  return {
    ...base,
    ...override,
    thresholds: {
      ...base.thresholds,
      ...(override.thresholds || {}),
    },
  };
}