using System.Diagnostics;
using System.IO;
using System.Text;
using AxCopilot.Models;

namespace AxCopilot.Services.Agent.SlashCommands;

/// <summary>
/// Phase 26: /init — 프로젝트 초기화.
/// CC의 /init과 동등: 코드베이스를 분석하여 AX.md(프로젝트 규칙)를 자동 생성합니다.
/// </summary>
public class InitCommand : ISlashCommand
{
    public string Name => "init";
    public string[] Aliases => Array.Empty<string>();
    public string Description => "프로젝트를 분석하여 AX.md 프로젝트 규칙을 자동 생성합니다";
    public string? ArgumentHint => null;
    public bool RequiresActiveSession => false;

    public async Task ExecuteAsync(string arguments, IAgentChatContext ctx, CancellationToken ct)
    {
        var workDir = ctx.WorkFolder;
        if (string.IsNullOrEmpty(workDir) || !Directory.Exists(workDir))
        {
            await ctx.SendSystemMessageAsync("작업 폴더가 설정되지 않았습니다.");
            return;
        }

        var axMdPath = Path.Combine(workDir, "AX.md");
        if (File.Exists(axMdPath))
        {
            await ctx.SendSystemMessageAsync($"AX.md가 이미 존재합니다: `{axMdPath}`\n기존 파일을 덮어쓰려면 삭제 후 다시 실행하세요.");
            return;
        }

        await ctx.SendSystemMessageAsync("프로젝트를 분석하고 있습니다...");

        // 1. 프로젝트 정보 수집
        var info = new StringBuilder();

        // 프로젝트 구조
        info.AppendLine("## 프로젝트 구조");
        var structure = GetDirectoryTree(workDir, maxDepth: 3, maxItems: 50);
        info.AppendLine($"```\n{structure}\n```");

        // Git 정보
        var gitDir = Path.Combine(workDir, ".git");
        if (Directory.Exists(gitDir))
        {
            info.AppendLine("\n## Git 정보");
            var branch = await RunCommandAsync("git", "branch --show-current", workDir, ct);
            var remotes = await RunCommandAsync("git", "remote -v", workDir, ct);
            var recentCommits = await RunCommandAsync("git", "log --oneline -5", workDir, ct);
            if (!string.IsNullOrWhiteSpace(branch)) info.AppendLine($"- 현재 브랜치: {branch.Trim()}");
            if (!string.IsNullOrWhiteSpace(remotes)) info.AppendLine($"- 리모트:\n```\n{remotes.Trim()}\n```");
            if (!string.IsNullOrWhiteSpace(recentCommits)) info.AppendLine($"- 최근 커밋:\n```\n{recentCommits.Trim()}\n```");
        }

        // 프레임워크/언어 감지
        info.AppendLine("\n## 감지된 기술 스택");
        var detectedStack = DetectTechStack(workDir);
        foreach (var item in detectedStack) info.AppendLine($"- {item}");

        // 주요 설정 파일 내용 (작은 것만)
        var configFiles = new[] { "package.json", "tsconfig.json", "*.csproj", "Cargo.toml", "go.mod", "pyproject.toml", "requirements.txt" };
        foreach (var pattern in configFiles)
        {
            var files = pattern.Contains('*')
                ? Directory.GetFiles(workDir, pattern, SearchOption.TopDirectoryOnly)
                : File.Exists(Path.Combine(workDir, pattern)) ? new[] { Path.Combine(workDir, pattern) } : Array.Empty<string>();

            foreach (var file in files.Take(2))
            {
                var content = File.ReadAllText(file, Encoding.UTF8);
                if (content.Length > 3000) content = content[..3000] + "\n...";
                info.AppendLine($"\n## {Path.GetFileName(file)}");
                info.AppendLine($"```\n{content.Trim()}\n```");
            }
        }

        // 2. LLM에게 AX.md 생성 요청
        var prompt = $"""
            아래 프로젝트 정보를 분석하여 AX.md (프로젝트 규칙 파일)를 생성하세요.

            AX.md는 AI 에이전트가 이 프로젝트에서 작업할 때 참고하는 규칙 파일입니다.
            다음 섹션을 포함하세요:

            1. **프로젝트 개요** — 프로젝트 이름, 목적, 기술 스택 요약 (2-3문장)
            2. **코드 컨벤션** — 언어/프레임워크에 맞는 명명 규칙, 파일 구조, 코딩 스타일
            3. **빌드 & 테스트** — 빌드 명령, 테스트 실행 방법, 린트/포맷 도구
            4. **작업 가이드라인** — PR 규칙, 커밋 메시지 형식, 브랜치 전략
            5. **주의사항** — 민감 파일, 수정 금지 영역, 환경별 차이

            마크다운 형식으로 작성하세요. AX.md 내용만 출력하세요 (다른 설명 없이).

            {info}
            """;

        try
        {
            var messages = new List<ChatMessage>
            {
                new() { Role = "user", Content = prompt }
            };
            var axMdContent = await ctx.Llm.SendAsync(messages, ct);

            // 마크다운 코드 블록 래핑 제거
            axMdContent = axMdContent.Trim();
            if (axMdContent.StartsWith("```markdown"))
                axMdContent = axMdContent[11..];
            else if (axMdContent.StartsWith("```"))
                axMdContent = axMdContent[3..];
            if (axMdContent.EndsWith("```"))
                axMdContent = axMdContent[..^3];
            axMdContent = axMdContent.Trim();

            File.WriteAllText(axMdPath, axMdContent, Encoding.UTF8);
            await ctx.SendSystemMessageAsync(
                $"## AX.md 생성 완료\n\n`{axMdPath}`에 프로젝트 규칙이 생성되었습니다.\n내용을 검토하고 필요에 따라 수정하세요.");
        }
        catch (Exception ex)
        {
            await ctx.SendSystemMessageAsync($"AX.md 생성 실패: {ex.Message}");
        }
    }

    private static string GetDirectoryTree(string rootDir, int maxDepth, int maxItems)
    {
        var sb = new StringBuilder();
        var count = 0;
        BuildTree(sb, rootDir, "", maxDepth, 0, ref count, maxItems);
        return sb.ToString();
    }

    private static void BuildTree(StringBuilder sb, string dir, string indent, int maxDepth, int depth, ref int count, int maxItems)
    {
        if (depth >= maxDepth || count >= maxItems) return;

        var skipDirs = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
        {
            "node_modules", ".git", "bin", "obj", ".vs", ".idea", "__pycache__",
            "venv", ".venv", "dist", "build", ".next", "target", ".cache"
        };

        try
        {
            var entries = Directory.GetFileSystemEntries(dir)
                .Select(e => new { Path = e, IsDir = Directory.Exists(e), Name = Path.GetFileName(e) })
                .Where(e => !e.IsDir || !skipDirs.Contains(e.Name))
                .OrderByDescending(e => e.IsDir)
                .ThenBy(e => e.Name)
                .ToList();

            foreach (var entry in entries)
            {
                if (count >= maxItems) { sb.AppendLine($"{indent}... ({entries.Count - count}+ more)"); break; }
                sb.AppendLine($"{indent}{(entry.IsDir ? "📁 " : "  ")}{entry.Name}");
                count++;
                if (entry.IsDir)
                    BuildTree(sb, entry.Path, indent + "  ", maxDepth, depth + 1, ref count, maxItems);
            }
        }
        catch (Exception) { }
    }

    private static List<string> DetectTechStack(string dir)
    {
        var stack = new List<string>();
        bool Has(string file) => File.Exists(Path.Combine(dir, file));
        bool HasPattern(string pattern) => Directory.GetFiles(dir, pattern, SearchOption.TopDirectoryOnly).Length > 0;

        if (Has("package.json")) stack.Add("Node.js / npm");
        if (Has("tsconfig.json")) stack.Add("TypeScript");
        if (HasPattern("*.csproj")) stack.Add(".NET / C#");
        if (HasPattern("*.sln")) stack.Add("Visual Studio Solution");
        if (Has("Cargo.toml")) stack.Add("Rust / Cargo");
        if (Has("go.mod")) stack.Add("Go");
        if (Has("pyproject.toml") || Has("setup.py") || Has("requirements.txt")) stack.Add("Python");
        if (Has("Gemfile")) stack.Add("Ruby");
        if (Has("pom.xml") || Has("build.gradle")) stack.Add("Java");
        if (Has("docker-compose.yml") || Has("Dockerfile")) stack.Add("Docker");
        if (Has(".github/workflows")) stack.Add("GitHub Actions CI");
        if (Has("Makefile")) stack.Add("Make");

        if (stack.Count == 0) stack.Add("(감지된 프레임워크 없음)");
        return stack;
    }

    private static async Task<string> RunCommandAsync(string cmd, string args, string workDir, CancellationToken ct)
    {
        try
        {
            using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct);
            cts.CancelAfter(TimeSpan.FromSeconds(10));

            var psi = new ProcessStartInfo
            {
                FileName = cmd,
                Arguments = args,
                WorkingDirectory = workDir,
                UseShellExecute = false,
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                CreateNoWindow = true,
                StandardOutputEncoding = Encoding.UTF8,
            };

            using var proc = Process.Start(psi);
            if (proc == null) return "";

            var output = await proc.StandardOutput.ReadToEndAsync(cts.Token);
            await proc.WaitForExitAsync(cts.Token);
            return output;
        }
        catch (Exception) { return ""; }
    }
}
