using System.Text.RegularExpressions;
using Microsoft.EntityFrameworkCore;
using SolutionErp.Application.Common.Exceptions;
using SolutionErp.Application.Common.Interfaces;
using SolutionErp.Domain.ContractSigningPlans;

namespace SolutionErp.Application.ContractSigningPlans;

// [S182 W1b — YC-013] Rào DÙNG CHUNG cho quan hệ "nhóm duyệt của phiếu" ⟷ "nhóm duyệt của
// quy trình đã pin". Trước đợt này logic chỉ nằm ở MỘT chỗ (đường TẠO phiếu,
// `ContractSigningPlanFeatures.cs` rào (v) — K3 S165) ⇒ hai cửa còn lại (sửa nháp re-pin,
// và TRÌNH duyệt) đi lọt: phiếu nhóm 3 vẫn trình được bằng quy trình nhóm 5.
//
// 🔴 VÌ SAO `public static class` ở tầng Application chứ không nhét vào
//    `ContractSigningPlanScope` (internal, cùng file Features):
//    site thứ 3 là `ContractSigningPlanWorkflowService.SubmitAsync` nằm ở **Infrastructure**
//    ⇒ không thấy `internal` của Application. Ba lối đã cân nhắc và loại:
//      • nới `Scope` thành public → mở TOÀN BỘ helper nội bộ ra ngoài, chỉ để dùng 1 method;
//      • `InternalsVisibleTo(Infrastructure)` → mở cả assembly, cùng bệnh, còn khó soi hơn;
//      • copy-inline sang Service → 2 bản luật ĐI LỆCH NHAU theo thời gian (đúng bug-class
//        đang chữa: một chỗ có rào, chỗ kia không).
//    ⇒ tách file riêng, đúng một method public, ba site cùng gọi.
public static class ContractSigningPlanWorkflowGuards
{
    // Họ mã quy trình nhóm duyệt: `KHKK-N1` … `KHKK-N8`, seed ở
    // `DbInitializer.SeedKhkkGroupWorkflowsAsync` (`DbInitializer.cs:609` — `$"KHKK-N{n}"`).
    //
    // 🔴 ĐÂY LÀ NHÀ DUY NHẤT của mẫu này phía Application. Trước đợt này mẫu được gõ tay
    //    trong `ContractSigningPlanFeatures.cs:482`; W1d cần lại đúng mẫu đó ở
    //    `ApprovalWorkflowV2AdminFeatures` (rào xoá version cuối) ⇒ nếu chép lần nữa thì có
    //    3 bản chuỗi rời nhau, sửa 1 quên 2. Ai cần "code này thuộc nhóm mấy" thì gọi
    //    `ParseGroup`, đừng viết lại regex.
    //
    // ⚠️ ĐỪNG nhầm với rào W1a bên `ApprovalWorkflowV2AdminFeatures` (deactivate-per-Code):
    //    ở ĐÓ regex bị CẤM (R-6) vì phạm vi tắt-active phải phủ MỌI code type-10, kể cả code
    //    ngoài họ. Ở ĐÂY regex mới là đúng việc: mục đích của rào là NHẬN DIỆN họ `KHKK-N`,
    //    và code ngoài họ được cho qua CÓ CHỦ ĐÍCH (xem `EnsureWorkflowGroupMatchAsync`).
    // 🔴 [S182 MAJOR-2] `IgnoreCase` = BELT cho dữ-liệu CŨ. Đường ghi nay đã chuẩn hoá
    //    `Code` về HOA (`CreateAwDefinitionCommandHandler` — `request.Code.Trim().ToUpperInvariant()`),
    //    nhưng row có TRƯỚC bản vá thì chưa qua cửa đó. Thiếu cờ này, một `khkk-n1` tồn dư
    //    sẽ trả `null` ⇒ rào nhóm 3-site + D5 im lặng TẮT cho cả nhóm 1, trong khi SQL Server
    //    (collation `*_CI_AS`) vẫn coi nó trùng `KHKK-N1` ⇒ hai tầng nói ngược nhau.
    //    Suspenders ⟂ belt: chuẩn-hoá chặn dữ-liệu MỚI, cờ này tha-thứ dữ-liệu CŨ.
    private static readonly Regex GroupCodePattern =
        new(@"^KHKK-N([1-8])$", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);

    /// Số nhóm duyệt suy ra từ mã quy trình, hoặc `null` nếu mã KHÔNG thuộc họ `KHKK-N[1-8]`.
    public static int? ParseGroup(string? workflowCode)
    {
        var m = GroupCodePattern.Match(workflowCode ?? string.Empty);
        return m.Success ? int.Parse(m.Groups[1].Value) : null;
    }

    /// Quy trình thuộc họ nhóm `KHKK-N{k}` ⇒ `k` PHẢI khớp nhóm duyệt của phiếu, nếu không → 409.
    ///
    /// 🔴 GIỮ NGUYÊN semantics "cho qua code NGOÀI họ" của bản gốc
    ///    (`ContractSigningPlanFeatures.cs:477-478` — chủ đích, không phải sót): ràng buộc này
    ///    chỉ nói về 8 workflow nhóm seed @K3. Quy trình type-10 đặt tay (vd `QT-KHKK-V2-001`)
    ///    không mang thông tin nhóm trong mã ⇒ không có gì để đối chiếu, chặn nó là bịa ràng
    ///    buộc. Rào loại (`EnsureWorkflowTypeAsync`) mới là chỗ chặn quy trình sai LOẠI.
    ///
    /// Mã không tồn tại (`awId` rác) cũng rơi vào nhánh cho-qua: guard NotFound là việc của
    /// `EnsureWorkflowTypeAsync` (Create/UpdateDraft) — chồng thêm ở đây sẽ đẻ 2 nguồn 404
    /// cho cùng một nguyên nhân.
    public static async Task EnsureWorkflowGroupMatchAsync(
        IApplicationDbContext db, Guid awId, int approvalGroup, CancellationToken ct)
    {
        var code = await db.ApprovalWorkflows.AsNoTracking()
            .Where(w => w.Id == awId)
            .Select(w => w.Code)
            .FirstOrDefaultAsync(ct);

        var wfGroup = ParseGroup(code);
        if (wfGroup is int k && k != approvalGroup)
            throw new ConflictException(
                $"Phiếu nhóm N{approvalGroup} đang gắn quy trình N{k} — " +
                "vào phiếu (Nháp/Trả lại) đổi lại quy trình đúng nhóm trước khi trình.");
    }

    /// [YC-016 @S183 — anh chốt "(c) THEO TAY CẦM PHIẾU"] File-khi-duyệt (`Purpose =
    /// ApprovalAttachment`) đi theo TAY đang cầm phiếu, không theo policy module:
    ///   • Nháp / Trả lại  (tay người soạn)  ⇒ CHỈ người soạn (`CreatedBy`);
    ///   • Chờ duyệt       (tay người duyệt) ⇒ CHỈ thành viên quy trình đã pin
    ///     (mọi `Level.ApproverUserId` của workflow — membership, KHÔNG đòi đúng-lượt:
    ///     chữ anh @S171 "file do người duyệt tải lên trong quá trình duyệt" không thu
    ///     hẹp về acting-level; siết thêm sau = 1 mệnh-đề);
    ///   • Đã duyệt / Từ chối (terminal — không ai cầm) ⇒ CHẶN cả hai (hồ sơ đã chốt).
    ///
    /// 🔴 Vì sao cần rào này dù endpoint đã có `[Authorize(Policy="KeHoachKyKet.Update")]`:
    ///    policy là quyền MODULE (người soạn đương nhiên có để soạn phiếu) ⇒ đo prod @S172:
    ///    `test.drafter@` up được file duyệt vào phiếu KHÔNG thuộc tay mình. Display-layer
    ///    (nút chỉ nằm trong dialog duyệt) không phải rào — #82 hai tầng độc lập.
    public static async Task EnsureApprovalAttachmentUploaderAsync(
        IApplicationDbContext db, ContractSigningPlan plan, Guid? userId, CancellationToken ct)
    {
        switch (plan.Phase)
        {
            case ContractSigningPlanPhase.DangSoanThao:
            case ContractSigningPlanPhase.TraLai:
                if (userId is null || plan.CreatedBy != userId)
                    throw new ConflictException(
                        "Phiếu đang ở tay người soạn (Nháp/Trả lại) — chỉ người soạn được đính kèm file duyệt.");
                return;

            case ContractSigningPlanPhase.ChoDuyet:
            {
                // Submit đã ép pin (`SubmitAsync` guard `ApprovalWorkflowId is not Guid ⇒ 409`)
                // nên nhánh null ở đây là fail-safe, không phải đường đi thường.
                if (plan.ApprovalWorkflowId is not Guid awId)
                    throw new ConflictException(
                        "Phiếu chưa gắn quy trình duyệt — không xác định được người duyệt.");

                var isMember = userId is Guid uid && await db.ApprovalWorkflowLevels
                    .AsNoTracking()
                    .AnyAsync(l => l.Step!.ApprovalWorkflowId == awId && l.ApproverUserId == uid, ct);
                if (!isMember)
                    throw new ConflictException(
                        "Phiếu đang chờ duyệt — chỉ người duyệt trong quy trình được đính kèm file khi duyệt.");
                return;
            }

            default: // DaDuyet / TuChoi — terminal, hồ sơ đã chốt
                throw new ConflictException(
                    "Phiếu đã kết thúc — hồ sơ đã chốt, không đính kèm thêm file duyệt.");
        }
    }
}
