using System.Transactions;
using SIGCM2.Application.Abstractions;
using SIGCM2.Application.Abstractions.Persistence;
using SIGCM2.Application.Audit;
using SIGCM2.Domain.Entities;
using SIGCM2.Domain.Exceptions;

namespace SIGCM2.Application.PuntosDeVenta.Create;

public sealed class CreatePuntoDeVentaCommandHandler : ICommandHandler<CreatePuntoDeVentaCommand, PuntoDeVentaCreatedDto>
{
    private readonly IPuntoDeVentaRepository _repo;
    private readonly IMedioRepository _medioRepo;
    private readonly IAuditLogger _audit;

    public CreatePuntoDeVentaCommandHandler(
        IPuntoDeVentaRepository repo,
        IMedioRepository medioRepo,
        IAuditLogger audit)
    {
        _repo = repo;
        _medioRepo = medioRepo;
        _audit = audit;
    }

    public async Task<PuntoDeVentaCreatedDto> Handle(CreatePuntoDeVentaCommand command)
    {
        // Validate medio exists and is active (REQ-PDV-001, -002)
        var medio = await _medioRepo.GetByIdAsync(command.MedioId)
            ?? throw new MedioNotFoundException(command.MedioId);

        if (!medio.Activo)
            throw new MedioInactivoException(medio.Id);

        // Check uniqueness NumeroAFIP within Medio (REQ-PDV-003)
        var exists = await _repo.ExistsByNumeroAFIPInMedioAsync(command.MedioId, command.NumeroAFIP, excludeId: null);
        if (exists)
            throw new NumeroAFIPDuplicadoException(command.MedioId, command.NumeroAFIP);

        var pdv = PuntoDeVenta.ForCreation(command.MedioId, command.NumeroAFIP, command.Nombre, command.Descripcion);

        using var tx = new TransactionScope(
            TransactionScopeOption.Required,
            new TransactionOptions { IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted },
            TransactionScopeAsyncFlowOption.Enabled);

        var newId = await _repo.AddAsync(pdv);

        // fail-closed: if LogAsync throws, tx rolls back (REQ-PDV-010)
        await _audit.LogAsync(
            action: "punto_de_venta.create",
            targetType: "PuntoDeVenta",
            targetId: newId.ToString(),
            metadata: new
            {
                after = new
                {
                    pdv.MedioId,
                    pdv.NumeroAFIP,
                    pdv.Nombre,
                    pdv.Descripcion,
                },
            });

        tx.Complete();

        return new PuntoDeVentaCreatedDto(
            Id: newId,
            MedioId: pdv.MedioId,
            NumeroAFIP: pdv.NumeroAFIP,
            Nombre: pdv.Nombre,
            Descripcion: pdv.Descripcion,
            Activo: pdv.Activo);
    }
}
