using System.Diagnostics;
using System.IO;
using System.Text;

namespace AxCopilot.Services.Agent;

/// <summary>git worktree 정보.</summary>
public record Worktree
{
    public string Path { get; init; } = "";
    public string Branch { get; init; } = "";
    public string RepoRoot { get; init; } = "";
    public DateTime CreatedAt { get; init; } = DateTime.UtcNow;
}

/// <summary>
/// Phase 18-A: git worktree 기반 격리 실행 환경 관리.
/// 서브에이전트에 독립적인 git working copy 제공.
/// 변경 사항은 승인 전 메인 워킹트리에 영향 없음.
/// </summary>
public class WorktreeManager : IDisposable
{
    private readonly List<Worktree> _active = new();

    /// <summary>현재 활성 worktree 목록.</summary>
    public IReadOnlyList<Worktree> Active => _active;

    /// <summary>새 worktree 생성 (임시 브랜치).</summary>
    public async Task<Worktree?> CreateAsync(
        string repoRoot,
        string branchName,
        CancellationToken ct = default)
    {
        if (!Directory.Exists(repoRoot)) return null;

        // git이 설치되어 있는지 확인
        if (!await IsGitRepoAsync(repoRoot, ct)) return null;

        var worktreePath = System.IO.Path.Combine(
            System.IO.Path.GetTempPath(),
            "ax-worktree-" + Guid.NewGuid().ToString("N")[..8]);

        var result = await RunGitAsync(
            $"worktree add \"{worktreePath}\" -b \"{branchName}\"",
            repoRoot, ct);

        if (!result.Success) return null;

        var worktree = new Worktree
        {
            Path = worktreePath,
            Branch = branchName,
            RepoRoot = repoRoot
        };
        _active.Add(worktree);
        return worktree;
    }

    /// <summary>worktree 변경 사항을 대상 브랜치에 병합 (승인 시).</summary>
    public async Task<bool> MergeAsync(
        Worktree worktree,
        string targetBranch,
        CancellationToken ct = default)
    {
        // 메인 워킹트리에서 머지
        var result = await RunGitAsync(
            $"merge \"{worktree.Branch}\" --no-ff -m \"[AX Agent] {worktree.Branch} 병합\"",
            worktree.RepoRoot, ct);
        return result.Success;
    }

    /// <summary>worktree 제거 (거부 시 또는 완료 후 정리).</summary>
    public async Task DisposeWorktreeAsync(
        Worktree worktree,
        bool keepBranch = false,
        CancellationToken ct = default)
    {
        try
        {
            await RunGitAsync(
                $"worktree remove \"{worktree.Path}\" --force",
                worktree.RepoRoot, ct);

            if (!keepBranch)
                await RunGitAsync($"branch -D \"{worktree.Branch}\"", worktree.RepoRoot, ct);
        }
        catch (Exception) { }
        finally { _active.Remove(worktree); }
    }

    private static async Task<bool> IsGitRepoAsync(string dir, CancellationToken ct)
    {
        var result = await RunGitAsync("rev-parse --git-dir", dir, ct);
        return result.Success;
    }

    private static async Task<(bool Success, string Output)> RunGitAsync(
        string args, string cwd, CancellationToken ct)
    {
        try
        {
            var psi = new ProcessStartInfo("git", args)
            {
                WorkingDirectory = cwd,
                UseShellExecute = false,
                CreateNoWindow = true,
                RedirectStandardOutput = true,
                RedirectStandardError = true,
            };

            using var process = new Process { StartInfo = psi };
            var sb = new StringBuilder();
            process.OutputDataReceived += (_, e) => { if (e.Data != null) sb.AppendLine(e.Data); };
            process.ErrorDataReceived  += (_, e) => { if (e.Data != null) sb.AppendLine(e.Data); };
            process.Start();
            process.BeginOutputReadLine();
            process.BeginErrorReadLine();

            using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct);
            cts.CancelAfter(TimeSpan.FromSeconds(30));
            await process.WaitForExitAsync(cts.Token);

            return (process.ExitCode == 0, sb.ToString());
        }
        catch (Exception) { return (false, ""); }
    }

    public void Dispose()
    {
        // 남은 worktree 정리 (비동기이지만 Dispose에서 동기로 처리)
        foreach (var wt in _active.ToList())
        {
            try
            {
                RunGitAsync($"worktree remove \"{wt.Path}\" --force", wt.RepoRoot, CancellationToken.None)
                    .GetAwaiter().GetResult();
            }
            catch (Exception) { }
        }
        _active.Clear();
    }
}
