using AgentSmith.Application.Models;
using AgentSmith.Application.Services.Lifecycle;
using AgentSmith.Contracts.Commands;
using AgentSmith.Contracts.Providers;
using AgentSmith.Contracts.Sandbox;
using AgentSmith.Domain.Models;
using Microsoft.Extensions.Logging;

namespace AgentSmith.Application.Services.Handlers;

/// <summary>
/// Commits changes (in the sandbox where the modifications live) and creates a
/// pull request via the source provider's API. If a DoneStatus is configured
/// (e.g. from a Jira webhook trigger), transitions the ticket to that status;
/// otherwise closes the ticket.
/// </summary>
public sealed class CommitAndPRHandler(
    ISourceProviderFactory sourceFactory,
    ITicketProviderFactory ticketFactory,
    SandboxGitOperations gitOps,
    ILogger<CommitAndPRHandler> logger)
    : ICommandHandler<CommitAndPRContext>
{
    public async Task<CommandResult> ExecuteAsync(
        CommitAndPRContext context, CancellationToken cancellationToken)
    {
        logger.LogInformation(
            "Creating PR for ticket {Ticket} with {Changes} changes...",
            context.Ticket.Id, context.Changes.Count);

        if (!context.Pipeline.TryGet<ISandbox>(ContextKeys.Sandbox, out var sandbox) || sandbox is null)
            return CommandResult.Fail("CommitAndPR requires an active sandbox; none in pipeline context.");

        var sourceProvider = sourceFactory.Create(context.RepoConnection);

        var message = $"fix: {context.Ticket.Title} (#{context.Ticket.Id})";
        await gitOps.CommitAndPushAsync(
            sandbox, context.Repository.CurrentBranch.Value, message,
            context.RepoConnection.Type, cancellationToken);

        var prUrl = await sourceProvider.CreatePullRequestAsync(
            context.Repository,
            context.Ticket.Title,
            context.Ticket.Description,
            cancellationToken,
            linkedTicketId: context.Ticket.Id);

        context.Pipeline.Set(ContextKeys.PullRequestUrl, prUrl);

        logger.LogInformation("Pull request created: {Url}", prUrl);

        await FinalizeTicketAsync(context, prUrl, cancellationToken);

        return CommandResult.Ok($"Pull request created: {prUrl}");
    }

    private Task FinalizeTicketAsync(
        CommitAndPRContext context, string prUrl, CancellationToken cancellationToken)
    {
        var changes = string.Join("\n",
            context.Changes.Select(c => $"- [{c.ChangeType}] `{c.Path}`"));

        var summary = $"""
            ## Agent Smith - Completed in {context.RepoConnection.Name}

            **PR:** {prUrl}

            ### Changes
            {changes}

            This ticket was automatically processed by Agent Smith.
            """;

        context.Pipeline.TryGet<string>(ContextKeys.DoneStatus, out var doneStatus);

        return TicketLifecycle.FinalizeAsync(
            ticketFactory, context.TrackerConnection, context.Ticket.Id,
            doneStatus, summary, logger, cancellationToken);
    }
}
