/**
 * Commit Message 生成器
 *
 * 参考 aider 的 message_generator 实现
 * 支持 conventional、simple、detailed 三种格式
 */

import * as path from 'path';
import type { DiffResult, MessageFormatConfig, FileDiff } from './types.js';

export class MessageGenerator {
  private config: MessageFormatConfig;

  constructor(config: MessageFormatConfig) {
    this.config = config;
  }

  /**
   * 生成 commit message
   */
  generate(diff: DiffResult, files?: string[]): string {
    const fileList = files || diff.files.map((f) => f.path);

    switch (this.config.style) {
      case 'conventional':
        return this.generateConventional(diff, fileList);
      case 'simple':
        return this.generateSimple(diff, fileList);
      case 'detailed':
        return this.generateDetailed(diff, fileList);
      default:
        return this.generateSimple(diff, fileList);
    }
  }

  /**
   * Conventional Commits 格式
   * type(scope): subject
   */
  private generateConventional(diff: DiffResult, files: string[]): string {
    const type = this.detectChangeType(diff, files);
    const scope = this.detectScope(files);
    const subject = this.generateSubject(diff, files);

    let message = type;
    if (scope) {
      message += `(${scope})`;
    }
    message += `: ${subject}`;

    // 截断到最大长度
    if (message.length > this.config.maxLength) {
      message = message.slice(0, this.config.maxLength - 3) + '...';
    }

    // 添加文件列表
    if (this.config.includeFileList && files.length <= 5) {
      const fileListStr = files.map((f) => `- ${f}`).join('\n');
      message += `\n\nFiles:\n${fileListStr}`;
    }

    return message;
  }

  /**
   * 简单格式
   * action file(s)
   */
  private generateSimple(diff: DiffResult, files: string[]): string {
    const action = this.detectAction(diff, files);

    if (files.length === 1) {
      return `${action} ${path.basename(files[0])}`;
    } else if (files.length <= 3) {
      const fileNames = files.map((f) => path.basename(f));
      return `${action} ${fileNames.join(', ')}`;
    } else {
      return `${action} ${files.length} files`;
    }
  }

  /**
   * 详细格式
   * type(scope): subject
   *
   * body with file list
   *
   * footer with stats
   */
  private generateDetailed(diff: DiffResult, files: string[]): string {
    const header = this.generateConventional(diff, files).split('\n')[0];
    const body = this.generateBody(diff, files);
    const footer = this.generateFooter(diff);

    return [header, '', body, '', footer].filter(Boolean).join('\n');
  }

  /**
   * 检测变更类型
   */
  private detectChangeType(diff: DiffResult, files: string[]): string {
    const hasNewFiles = diff.files.some((f) => f.status === 'added');
    const hasDeletedFiles = diff.files.some((f) => f.status === 'deleted');

    // 检查特殊文件类型
    const hasTestFiles = files.some((f) =>
      f.includes('test') || f.includes('spec') || f.includes('.test.')
    );
    const hasDocFiles = files.some((f) =>
      f.match(/\.(md|txt|rst|doc)$/i)
    );
    const hasConfigFiles = files.some((f) =>
      f.match(/(config|\.json|\.yaml|\.yml|\.toml|\.ini)$/i) ||
      f.includes('package.json') ||
      f.includes('tsconfig')
    );
    const hasStyleFiles = files.some((f) =>
      f.match(/\.(css|scss|less|sass|styl)$/i)
    );

    // 根据文件类型判断
    if (hasTestFiles && files.every((f) => f.includes('test') || f.includes('spec'))) {
      return 'test';
    }
    if (hasDocFiles && files.every((f) => f.match(/\.(md|txt|rst|doc)$/i))) {
      return 'docs';
    }
    if (hasConfigFiles && files.every((f) =>
      f.match(/(config|\.json|\.yaml|\.yml|\.toml|\.ini)$/i) ||
      f.includes('package.json')
    )) {
      return 'chore';
    }
    if (hasStyleFiles && files.every((f) => f.match(/\.(css|scss|less|sass|styl)$/i))) {
      return 'style';
    }

    // 根据变更类型判断
    if (hasNewFiles && !hasDeletedFiles) {
      return 'feat';
    }
    if (hasDeletedFiles && !hasNewFiles) {
      return 'refactor';
    }

    // 分析内容判断是 fix 还是 feat
    const content = diff.files
      .map((f) => f.hunks.map((h) => h.content).join(''))
      .join('');

    if (
      content.toLowerCase().includes('fix') ||
      content.toLowerCase().includes('bug') ||
      content.toLowerCase().includes('error') ||
      content.toLowerCase().includes('issue')
    ) {
      return 'fix';
    }

    // 默认为 feat
    return 'feat';
  }

  /**
   * 检测范围 (scope)
   */
  private detectScope(files: string[]): string | null {
    if (files.length === 0) return null;

    // 找共同目录
    const dirs = files.map((f) => {
      const parts = f.split('/');
      // 排除 src 目录，取第一个有意义的目录
      return parts.filter((p) => p !== 'src' && p !== '.' && !p.includes('.'))[0];
    });

    // 如果所有文件在同一目录下
    const uniqueDirs = [...new Set(dirs.filter(Boolean))];
    if (uniqueDirs.length === 1) {
      return uniqueDirs[0];
    }

    return null;
  }

  /**
   * 生成主题行
   */
  private generateSubject(diff: DiffResult, files: string[]): string {
    const action = this.detectAction(diff, files);

    if (files.length === 1) {
      const fileName = path.basename(files[0]);
      const ext = path.extname(fileName);
      const name = path.basename(fileName, ext);
      return `${action} ${name}`;
    }

    // 找共同特征
    const extensions = [...new Set(files.map((f) => path.extname(f)))];
    if (extensions.length === 1 && extensions[0]) {
      return `${action} ${files.length} ${extensions[0].slice(1)} files`;
    }

    const dirs = [...new Set(files.map((f) => f.split('/')[0]))];
    if (dirs.length === 1 && dirs[0] !== '.') {
      return `${action} ${dirs[0]} module`;
    }

    return `${action} ${files.length} files`;
  }

  /**
   * 检测操作动词
   */
  private detectAction(diff: DiffResult, files: string[]): string {
    const hasAdded = diff.files.some((f) => f.status === 'added');
    const hasDeleted = diff.files.some((f) => f.status === 'deleted');
    const hasModified = diff.files.some((f) => f.status === 'modified');
    const hasRenamed = diff.files.some((f) => f.status === 'renamed');

    if (hasAdded && !hasModified && !hasDeleted) return 'add';
    if (hasDeleted && !hasModified && !hasAdded) return 'remove';
    if (hasRenamed) return 'rename';
    if (hasModified && !hasAdded && !hasDeleted) return 'update';

    return 'update';
  }

  /**
   * 生成正文
   */
  private generateBody(diff: DiffResult, files: string[]): string {
    const changes: string[] = [];

    for (const file of files) {
      const fileDiff = diff.files.find((f) => f.path === file);
      const action = fileDiff
        ? this.getActionWord(fileDiff.status)
        : 'Modified';
      changes.push(`- ${action}: ${file}`);
    }

    return changes.join('\n');
  }

  /**
   * 获取操作词
   */
  private getActionWord(status: string): string {
    switch (status) {
      case 'added':
        return 'Added';
      case 'deleted':
        return 'Removed';
      case 'renamed':
        return 'Renamed';
      case 'copied':
        return 'Copied';
      case 'modified':
      default:
        return 'Modified';
    }
  }

  /**
   * 生成页脚
   */
  private generateFooter(diff: DiffResult): string {
    const { stats } = diff;
    if (stats.filesChanged === 0) {
      return '';
    }
    return `Stats: ${stats.filesChanged} file(s), +${stats.insertions}/-${stats.deletions}`;
  }
}
