using Books.Api.Domain.FiscalYears;
using Books.Api.Domain.FiscalYears.Events;
using EventFlow.Aggregates;
using EventFlow.Subscribers;
using Microsoft.Extensions.Logging;
using Npgsql;

namespace Books.Api.Ledger;

/// <summary>
/// Async subscriber that syncs fiscal years from Books.Api to Ledger's accounting_periods table.
/// This ensures every fiscal year has a corresponding Ledger period for posting transactions.
/// </summary>
public class LedgerPeriodSyncSubscriber(
    NpgsqlDataSource dataSource,
    ILogger<LedgerPeriodSyncSubscriber> logger)
    : ISubscribeAsynchronousTo<FiscalYearAggregate, FiscalYearId, FiscalYearCreatedEvent>
{
    private const string InsertPeriodSql = """
        INSERT INTO accounting_periods (id, name, start_date, end_date, is_locked, created_at)
        VALUES ($1, $2, $3, $4, false, now())
        ON CONFLICT (id) DO NOTHING
        """;

    public async Task HandleAsync(
        IDomainEvent<FiscalYearAggregate, FiscalYearId, FiscalYearCreatedEvent> domainEvent,
        CancellationToken cancellationToken)
    {
        var e = domainEvent.AggregateEvent;
        var fiscalYearIdString = domainEvent.AggregateIdentity.Value;

        // Extract UUID from FiscalYearId format "fiscalyear-{guid}"
        if (!TryParseFiscalYearGuid(fiscalYearIdString, out var periodGuid))
        {
            logger.LogWarning(
                "Cannot sync fiscal year {FiscalYearId} to Ledger - invalid ID format",
                fiscalYearIdString);
            return;
        }

        try
        {
            await using var conn = await dataSource.OpenConnectionAsync(cancellationToken);
            await using var cmd = new NpgsqlCommand(InsertPeriodSql, conn)
            {
                Parameters =
                {
                    new() { Value = periodGuid },
                    new() { Value = e.Name },
                    new() { Value = e.StartDate },
                    new() { Value = e.EndDate }
                }
            };

            var rowsAffected = await cmd.ExecuteNonQueryAsync(cancellationToken);

            if (rowsAffected > 0)
            {
                logger.LogInformation(
                    "Synced fiscal year {FiscalYearName} ({StartDate} - {EndDate}) to Ledger accounting_periods with ID {PeriodId}",
                    e.Name, e.StartDate, e.EndDate, periodGuid);
            }
            else
            {
                logger.LogDebug(
                    "Fiscal year {FiscalYearName} already exists in Ledger accounting_periods (ID {PeriodId})",
                    e.Name, periodGuid);
            }
        }
        catch (Exception ex)
        {
            logger.LogError(ex,
                "Failed to sync fiscal year {FiscalYearId} ({FiscalYearName}) to Ledger",
                fiscalYearIdString, e.Name);
            throw; // Re-throw to trigger retry via resilience strategy
        }
    }

    private static bool TryParseFiscalYearGuid(string fiscalYearId, out Guid guid)
    {
        guid = Guid.Empty;

        // FiscalYearId format: "fiscalyear-{guid}"
        if (!fiscalYearId.StartsWith("fiscalyear-", StringComparison.Ordinal))
            return false;

        var guidString = fiscalYearId["fiscalyear-".Length..];
        return Guid.TryParse(guidString, out guid);
    }
}
