import type { RepomixConfigMerged } from '../../config/configSchema.js';
import { withMemoryLogging } from '../../shared/memoryUtils.js';
import type { RepomixProgressCallback } from '../../shared/types.js';
import type { SkippedFileInfo } from '../file/fileCollect.js';
import type { ProcessedFile } from '../file/fileTypes.js';
import type { GitDiffResult } from '../git/gitDiffHandle.js';
import type { GitLogResult } from '../git/gitLogHandle.js';
import { calculateMetrics } from '../metrics/calculateMetrics.js';
import { buildOutputGeneratorContext, createRenderContext } from '../output/outputGenerate.js';
import { sortOutputFiles } from '../output/outputSort.js';
import type { PackOptions, PackResult } from '../packager.js';
import type { SuspiciousFileResult } from '../security/securityCheck.js';
import { generateFilesSection, generateStructureSection, generateSummarySection } from './skillSectionGenerators.js';
import { calculateStatistics, generateStatisticsSection } from './skillStatistics.js';
import { generateSkillMd } from './skillStyle.js';
import { detectTechStack, generateTechStackMd } from './skillTechStack.js';
import {
  generateDefaultSkillName,
  generateProjectName,
  generateSkillDescription,
  validateSkillName,
} from './skillUtils.js';
import { writeSkillOutput } from './writeSkillOutput.js';

/**
 * References for skill output - each becomes a separate file
 */
export interface SkillReferences {
  summary: string;
  structure: string;
  files: string;
  techStack?: string;
}

/**
 * Result of skill references generation (without SKILL.md)
 */
export interface SkillReferencesResult {
  references: SkillReferences;
  skillName: string;
  projectName: string;
  skillDescription: string;
  totalFiles: number;
  totalLines: number;
  statisticsSection: string;
  hasTechStack: boolean;
  sourceUrl?: string;
}

/**
 * Result of skill output generation
 */
export interface SkillOutputResult {
  skillMd: string;
  references: SkillReferences;
}

const defaultDeps = {
  buildOutputGeneratorContext,
  sortOutputFiles,
  calculateMetrics,
  writeSkillOutput,
  generateDefaultSkillName,
};

/**
 * Generates skill reference files (summary, structure, files, tech-stacks).
 * This is the first step - call this, calculate metrics, then call generateSkillMdFromReferences.
 */
export const generateSkillReferences = async (
  skillName: string,
  rootDirs: string[],
  config: RepomixConfigMerged,
  processedFiles: ProcessedFile[],
  allFilePaths: string[],
  gitDiffResult: GitDiffResult | undefined = undefined,
  gitLogResult: GitLogResult | undefined = undefined,
  skillProjectName?: string,
  skillSourceUrl?: string,
  deps = {
    buildOutputGeneratorContext,
    sortOutputFiles,
  },
): Promise<SkillReferencesResult> => {
  // Validate and normalize skill name
  const normalizedSkillName = validateSkillName(skillName);

  // Use provided project name or generate from root directories
  const projectName = skillProjectName ?? generateProjectName(rootDirs);

  // Generate skill description
  const skillDescription = generateSkillDescription(normalizedSkillName, projectName);

  // Sort processed files by git change count if enabled
  const sortedProcessedFiles = await deps.sortOutputFiles(processedFiles, config);

  // Build output generator context with markdown style
  const markdownConfig: RepomixConfigMerged = {
    ...config,
    output: {
      ...config.output,
      style: 'markdown',
    },
  };

  const outputGeneratorContext = await deps.buildOutputGeneratorContext(
    rootDirs,
    markdownConfig,
    allFilePaths,
    sortedProcessedFiles,
    gitDiffResult,
    gitLogResult,
  );
  const renderContext = createRenderContext(outputGeneratorContext);

  // Calculate statistics
  const statistics = calculateStatistics(sortedProcessedFiles, renderContext.fileLineCounts);
  const statisticsSection = generateStatisticsSection(statistics);

  // Detect tech stack
  const techStacks = detectTechStack(sortedProcessedFiles);
  const techStackMd = techStacks.length > 0 ? generateTechStackMd(techStacks) : undefined;

  // Generate each section separately
  const references: SkillReferences = {
    summary: generateSummarySection(renderContext, statisticsSection),
    structure: generateStructureSection(renderContext),
    files: generateFilesSection(renderContext),
    techStack: techStackMd,
  };

  return {
    references,
    skillName: normalizedSkillName,
    projectName,
    skillDescription,
    totalFiles: sortedProcessedFiles.length,
    totalLines: statistics.totalLines,
    statisticsSection,
    hasTechStack: techStacks.length > 0,
    sourceUrl: skillSourceUrl,
  };
};

/**
 * Generates SKILL.md content from references result and token count.
 * This is the second step - call after calculating metrics.
 */
export const generateSkillMdFromReferences = (
  referencesResult: SkillReferencesResult,
  totalTokens: number,
): SkillOutputResult => {
  const skillMd = generateSkillMd({
    skillName: referencesResult.skillName,
    skillDescription: referencesResult.skillDescription,
    projectName: referencesResult.projectName,
    totalFiles: referencesResult.totalFiles,
    totalLines: referencesResult.totalLines,
    totalTokens,
    hasTechStack: referencesResult.hasTechStack,
    sourceUrl: referencesResult.sourceUrl,
  });

  return {
    skillMd,
    references: referencesResult.references,
  };
};

export interface PackSkillParams {
  rootDirs: string[];
  config: RepomixConfigMerged;
  options: PackOptions;
  processedFiles: ProcessedFile[];
  allFilePaths: string[];
  gitDiffResult: GitDiffResult | undefined;
  gitLogResult: GitLogResult | undefined;
  suspiciousFilesResults: SuspiciousFileResult[];
  suspiciousGitDiffResults: SuspiciousFileResult[];
  suspiciousGitLogResults: SuspiciousFileResult[];
  safeFilePaths: string[];
  skippedFiles: SkippedFileInfo[];
  progressCallback: RepomixProgressCallback;
}

/**
 * Generates skill output (SKILL.md and reference files).
 * This is called from packager.ts when skill generation is requested.
 */
export const packSkill = async (params: PackSkillParams, deps = defaultDeps): Promise<PackResult> => {
  const {
    rootDirs,
    config,
    options,
    processedFiles,
    allFilePaths,
    gitDiffResult,
    gitLogResult,
    suspiciousFilesResults,
    suspiciousGitDiffResults,
    suspiciousGitLogResults,
    safeFilePaths,
    skippedFiles,
    progressCallback,
  } = params;

  // Validate skillDir early to fail fast (before expensive operations)
  const { skillDir } = options;
  if (!skillDir) {
    throw new Error('skillDir is required for skill generation');
  }

  // Use pre-computed skill name or generate from directories
  const skillName =
    options.skillName ??
    (typeof config.skillGenerate === 'string' ? config.skillGenerate : deps.generateDefaultSkillName(rootDirs));

  // Step 1: Generate skill references (summary, structure, files, tech-stacks)
  const skillReferencesResult = await withMemoryLogging('Generate Skill References', () =>
    generateSkillReferences(
      skillName,
      rootDirs,
      config,
      processedFiles,
      allFilePaths,
      gitDiffResult,
      gitLogResult,
      options.skillProjectName,
      options.skillSourceUrl,
      {
        buildOutputGeneratorContext: deps.buildOutputGeneratorContext,
        sortOutputFiles: deps.sortOutputFiles,
      },
    ),
  );

  // Step 2: Calculate metrics from files section to get accurate token count
  const skillMetrics = await withMemoryLogging('Calculate Skill Metrics', () =>
    deps.calculateMetrics(
      processedFiles,
      Promise.resolve(skillReferencesResult.references.files),
      progressCallback,
      config,
      gitDiffResult,
      gitLogResult,
    ),
  );

  // Step 3: Generate SKILL.md with accurate token count
  const skillOutput = generateSkillMdFromReferences(skillReferencesResult, skillMetrics.totalTokens);

  progressCallback('Writing skill output...');
  await withMemoryLogging('Write Skill Output', () => deps.writeSkillOutput(skillOutput, skillDir));

  return {
    ...skillMetrics,
    suspiciousFilesResults,
    suspiciousGitDiffResults,
    suspiciousGitLogResults,
    processedFiles,
    safeFilePaths,
    skippedFiles,
  };
};
