using System.Data;
using Microsoft.EntityFrameworkCore;
using SolutionErp.Application.Common.Interfaces;
using SolutionErp.Application.Contracts.Services;
using SolutionErp.Domain.Contracts;

namespace SolutionErp.Infrastructure.Services;

public class ContractCodeGenerator(IApplicationDbContext db, IDateTime dateTime) : IContractCodeGenerator
{
    public async Task<string> GenerateAsync(Contract contract, string projectCode, string supplierCode, CancellationToken ct = default)
    {
        // Format theo RG-001 (xem docs/forms-spec.md):
        //   HĐTP: {Project}/HĐTP/SOL&{Partner}/{Seq}
        //   HĐGK: {Project}/HĐGK/SOL&{Partner}/{Seq}
        //   NCC:  {Project}/NCC/SOL&{Partner}/{Seq}     (hoặc {Year}/NCC/SOL&{Partner}/{Seq} cho framework)
        //   HĐDV: {Project}/HĐDV/SOL&{Partner}/{Seq}    (hoặc {Year}/HĐDV/...)
        //   HĐ Mua bán: dùng PO format
        var typeCode = contract.Type switch
        {
            ContractType.HopDongThauPhu => "HĐTP",
            ContractType.HopDongGiaoKhoan => "HĐGK",
            ContractType.HopDongNhaCungCap => "NCC",
            ContractType.HopDongDichVu => "HĐDV",
            ContractType.HopDongMuaBan => "MB",
            ContractType.HopDongNguyenTacNCC => "NCC",
            ContractType.HopDongNguyenTacDichVu => "HĐDV",
            _ => "HĐ",
        };

        var isFramework = contract.Type is ContractType.HopDongNguyenTacNCC or ContractType.HopDongNguyenTacDichVu;
        var scope = isFramework ? dateTime.UtcNow.Year.ToString() : projectCode;
        var prefix = $"{scope}/{typeCode}/SOL&{supplierCode}";

        // Transaction SERIALIZABLE + UPDATE với lock
        var context = (DbContext)db;
        await using var tx = await context.Database.BeginTransactionAsync(IsolationLevel.Serializable, ct);
        try
        {
            var seq = await db.ContractCodeSequences.FirstOrDefaultAsync(s => s.Prefix == prefix, ct);
            if (seq is null)
            {
                seq = new ContractCodeSequence { Prefix = prefix, LastSeq = 1, UpdatedAt = dateTime.UtcNow };
                db.ContractCodeSequences.Add(seq);
            }
            else
            {
                seq.LastSeq += 1;
                seq.UpdatedAt = dateTime.UtcNow;
            }
            await db.SaveChangesAsync(ct);
            await tx.CommitAsync(ct);
            return $"{prefix}/{seq.LastSeq:D2}";
        }
        catch
        {
            await tx.RollbackAsync(ct);
            throw;
        }
    }
}
