using ClaudeDo.Data;
using ClaudeDo.Worker.Runner;

namespace ClaudeDo.Worker.Prime;

public sealed class PrimeRunner : IPrimeRunner
{
    private static readonly TimeSpan FireTimeout = TimeSpan.FromSeconds(60);
    private readonly IClaudeProcess _claude;
    private readonly ILogger<PrimeRunner> _logger;

    public PrimeRunner(IClaudeProcess claude, ILogger<PrimeRunner> logger)
    {
        _claude = claude;
        _logger = logger;
    }

    public async Task<PrimeRunOutcome> FireAsync(PrimeScheduleDto schedule, CancellationToken ct)
    {
        var cwd = Paths.AppDataRoot();
        Directory.CreateDirectory(cwd);

        using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(ct);
        timeoutCts.CancelAfter(FireTimeout);

        try
        {
            var prompt = schedule.PromptOverride ?? "ping";
            var result = await _claude.RunAsync(
                arguments: "-p --max-turns 1",
                prompt: prompt,
                workingDirectory: cwd,
                onStdoutLine: _ => Task.CompletedTask,
                ct: timeoutCts.Token);

            if (IsSuccess(result))
                return new PrimeRunOutcome(true, "Primed Claude");
            return new PrimeRunOutcome(false, FailureMessage(result));
        }
        catch (OperationCanceledException) when (timeoutCts.IsCancellationRequested && !ct.IsCancellationRequested)
        {
            return new PrimeRunOutcome(false, $"timed out after {FireTimeout.TotalSeconds:0}s");
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "Prime fire failed");
            return new PrimeRunOutcome(false, ex.Message);
        }
    }

    private static bool IsSuccess(RunResult result) => result.IsSuccess;
    private static string FailureMessage(RunResult result) => $"exit code {result.ExitCode}";
}
