using Application.Abstractions.Data;
using Application.Abstractions.Messaging;
using Application.Abstractions.Payment;
using Domain.Entities.Common.ValueObject;
using Domain.Entities.Payments.Danal;
using Domain.Entities.Payments.Danal.ValueObject;
using Domain.Entities.Payments.ValueObject;
using Microsoft.EntityFrameworkCore;

namespace Application.Features.Api.Payment.Cancel;

internal sealed class Handler(
    IAppDbContext db,
    IDanalPayService danalPay
) : ICommandHandler<Command>
{
    public async Task Handle(Command r, CancellationToken ct)
    {
        var order = await db.PaymentOrder.FirstOrDefaultAsync(o => o.OrderID == r.OrderID && o.MemberID == r.MemberID, ct);
        if (order is null)
        {
            throw new InvalidOperationException("주문을 찾을 수 없습니다.");
        }

        if (order.Status != PaymentStatus.Paid)
        {
            throw new InvalidOperationException("취소할 수 없는 주문 상태입니다.");
        }

        // 다날 결제수단 코드 매핑
        var method = order.PaymentMethod switch
        {
            PaymentMethod.Integrated => "INTEGRATED",
            PaymentMethod.Card => "CARD",
            PaymentMethod.VirtualAccount => "VACCOUNT",
            PaymentMethod.Mobile => "MOBILE",
            PaymentMethod.Transfer => "TRANSFER",
            PaymentMethod.NaverPay => "NAVERPAY",
            PaymentMethod.KakaoPay => "KAKAOPAY",
            PaymentMethod.Payco => "PAYCO",
            _ => "CARD"
        };

        // 취소 요청 기록 저장
        var cancel = DanalCancel.CreateRequest(r.MemberID, order.ID, order.PaymentMethod, order.TransactionID!, order.OrderID, order.Amount, order.MerchantID, DanalCancelType.Full);

        await db.DanalCancel.AddAsync(cancel, ct);
        await db.SaveChangesAsync(ct);

        var result = await danalPay.CancelAsync(method, order.TransactionID!, order.Amount, "C", ct);
        if (!result.Success)
        {
            cancel.SetResponse(result.Code ?? "", result.Message ?? "취소 실패");

            await db.DanalLog.AddAsync(DanalLog.Create(r.MemberID, DanalLogType.Failed, result.Code ?? "FAIL", result.Message ?? "취소 실패", order.TransactionID, order.OrderID), ct);
            await db.SaveChangesAsync(ct);

            throw new InvalidOperationException(result.Message ?? "결제 취소에 실패했습니다.");
        }

        // 취소 응답 저장 (Balance, RemainedAmount는 string? → int? 변환)
        cancel.SetResponse(
            result.Code ?? "", result.Message ?? "",
            result.OriginalTransactionID,
            result.CancelledAmount,
            result.TransDate, result.TransTime,
            int.TryParse(result.Balance, out var balanceVal) ? balanceVal : null,
            int.TryParse(result.RemainedAmount, out var remainedVal) ? remainedVal : null,
            result.ApprovalDateTime
        );

        order.MarkCancelled();

        // 지갑에서 PgCharged 차감
        var wallet = await db.Wallet.FirstOrDefaultAsync(w => w.MemberID == order.MemberID, ct);
        if (wallet is not null)
        {
            wallet.AdjustDecrease(Money.KRW(order.PointAmount), "결제 취소 환불", order.OrderID);
        }

        await db.SaveChangesAsync(ct);
    }
}