using Microsoft.EntityFrameworkCore;
using SolutionErp.Application.Common.Exceptions;
using SolutionErp.Application.Common.Interfaces;
using SolutionErp.Domain.ApprovalWorkflowsV2;
using SolutionErp.Domain.Contracts;

namespace SolutionErp.Application.Contracts;

// [S187 W4 — YC-023/YC-024] Rào DÙNG CHUNG cho quan hệ "loại Hợp đồng" ⟷ "quy trình duyệt V2 đã pin".
//
// Owner @S188: (86) KHOÁ CỨNG — máy tự pin quy trình đúng loại, KHÔNG cho đổi chéo loại
// (mirror KHKK/Duyệt NCC) · (80) trục 7 LOẠI HĐ · (81) PA-A (mỗi loại một mã riêng, 0 cột mới).
//
// 🔴 VÌ SAO `public static class` RIÊNG (FLAG-7 plan-review §6):
//    trước đợt này luật pin sống ở HAI bản inline đi lệch nhau —
//      • `ContractFeatures.cs:79-87` (Create): chỉ hỏi tồn-tại + đúng-ApplicableType;
//      • `CreateContractFromSigningPlanFeatures.cs:132-148` (cầu K7): 4 vế (+`IsActive` +`IsUserSelectable`).
//    Cửa thứ 3 (sửa nháp) thì KHÔNG có đường re-pin, cửa thứ 4 (TRÌNH duyệt) không kiểm gì.
//    Viết inline lần thứ 3 = nhân bản luật lần nữa. Site thứ 4 là `ContractWorkflowService.TransitionAsync`
//    nằm ở **Infrastructure** ⇒ không thấy `internal` của Application ⇒ phải `public static`,
//    đúng ba lối đã cân nhắc-và-loại ghi ở `ContractSigningPlanWorkflowGuards.cs:14-22`.
//
// 🔴 HÌNH DẠNG KHOÁ = theo KHÔNG-GIAN-TÊN `HD-`, không phải "bằng-nhau-tuyệt-đối":
//      code bắt đầu `HD-`  ⇒ PHẢI == `ContractApprovalWorkflowFamily.CodeFor(type)`;
//      code NGOÀI `HD-`    ⇒ CHO QUA, CÓ CHỦ ĐÍCH.
//    Lý do (giống hệt lý do của khuôn KHKK `ContractSigningPlanWorkflowGuards.cs:56-64`): ràng buộc này
//    chỉ nói về 7 quy trình họ `HD-*` seed @W3. Quy trình type-3 đặt tay (`QT-HD-V2-001` — bản DUY NHẤT
//    đang sống trên Dev đo 2026-08-10, và mọi workflow trong tests) KHÔNG mang thông tin loại trong mã
//    ⇒ không có gì để đối chiếu; chặn nó là bịa ràng buộc và giết luôn quyền admin tự tạo quy trình.
//    Vế "PHẢI == khi ở trong họ" mới là răng: pin `HD-MB` lên HĐ Thầu phụ = đổi chéo loại ⇒ 409.
//    Nó cũng bắt ca gõ nhầm TRONG họ (`HD-TPX`) thay vì cho trôi im lặng.
//
// 🔴 So sánh mã bằng `OrdinalIgnoreCase` ở CẢ 2 vế (tiền tố + bằng nhau) — bài S182 MAJOR-2:
//    SQL Server collation `*_CI_AS` coi `hd-tp` ≡ `HD-TP`, SQLite (test) so BINARY ⇒ hai tầng nói ngược
//    nhau nếu rào dùng so sánh case-SENSITIVE. Đường ghi đã chuẩn-hoá `Trim().ToUpperInvariant()`
//    (`ApprovalWorkflowV2AdminFeatures.cs` handler Create) — đó là belt cho dữ-liệu MỚI; cờ này là
//    suspenders cho dữ-liệu CŨ (row có trước bản chuẩn-hoá).
public static class ContractWorkflowGuards
{
    /// Rào ĐƯỜNG GHI (pin / re-pin): Create · cầu K7 · sửa nháp. 5 vế, message TÁCH RIÊNG từng
    /// nguyên nhân (gộp `||` thì người dùng không biết phải bấm nút nào).
    ///
    /// Thứ tự vế = từ "sai định danh" tới "sai vòng đời": tồn-tại → đúng loại module → đúng loại HĐ
    /// (khoá cứng) → còn hoạt động → còn ghim.
    ///
    /// 🔴 `ConflictException` chứ KHÔNG phải `ValidationException`: `ValidationException`
    ///    (`Common/Exceptions/ApplicationException.cs:10-25`) không có ctor nhận message ⇒ không mang
    ///    được câu tiếng Việt; và lưới hành-vi đang sống assert `ConflictException`
    ///    (`CreateContractCommandApplicableTypeTests.cs:88` + `CreateContractFromSigningPlanTests.cs:475-488`).
    ///    Nhánh id-rác giữ `NotFoundException` — lỗi tham chiếu, không phải lỗi trạng thái.
    public static async Task EnsureValidWorkflowForContractAsync(
        IApplicationDbContext db, Guid workflowId, ContractType contractType, CancellationToken ct)
    {
        var aw = await db.ApprovalWorkflows.AsNoTracking()
            .FirstOrDefaultAsync(w => w.Id == workflowId, ct)
            ?? throw new NotFoundException("ApprovalWorkflow", workflowId);

        // Vế 1 — đúng module. Message PHẢI chứa CẢ "không khớp với Hợp đồng" (lưới cầu K7) LẪN
        // "ApplicableType=Contract" (lưới Create): một nhà chung phát ngôn cho cả hai cửa.
        if (aw.ApplicableType != ApprovalWorkflowApplicableType.Contract)
            throw new ConflictException(
                $"Quy trình '{aw.Code}' áp dụng cho {aw.ApplicableType}, không khớp với Hợp đồng " +
                "(cần ApplicableType=Contract).");

        // Vế 2 — KHOÁ CỨNG LOẠI (86). Xem chú thích đầu class về hình dạng khoá.
        EnsureFamilyLock(aw.Code, contractType);

        // Vế 3+4 — vòng đời. 2 cờ TÁCH RIÊNG (không gộp): admin cần đọc được NGUYÊN NHÂN —
        // "đã nghỉ" và "chưa ghim" xử lý ở 2 nút khác nhau trong Designer.
        if (!aw.IsActive)
            throw new ConflictException(
                $"Quy trình '{aw.Code}' đã ngừng hoạt động — chọn quy trình đang hoạt động.");
        if (!aw.IsUserSelectable)
            throw new ConflictException(
                $"Quy trình '{aw.Code}' chưa được ghim cho người dùng chọn — nhờ Admin ghim, hoặc chọn quy trình khác.");
    }

    /// PA-A (81): tự tìm quy trình của ĐÚNG loại HĐ — `null` khi chưa seed / admin đã gỡ ghim.
    ///
    /// 🔴 Điều kiện lọc = ĐÚNG BỘ điều kiện mà `EnsureValidWorkflowForContractAsync` chấp nhận
    ///    (`IsActive` ∧ `IsUserSelectable` ∧ đúng type ∧ đúng Code): máy chỉ được tự pin thứ mà cửa
    ///    validate cũng cho pin, nếu không thì cùng một quy trình vừa được máy gắn vừa bị cửa sửa-nháp
    ///    từ chối — hai tầng nói ngược nhau.
    /// `null` KHÔNG phải lỗi: HĐ rơi về nhánh V1 legacy (`WorkflowDefinitionId`) đúng như hành vi
    ///    trước W4 khi người dùng không chọn gì. Cửa nào cần "bắt buộc phải có" thì tự ném (cầu K7).
    /// `OrderByDescending(Version)` cho xác định: sau W1a (deactivate per-Code) chỉ 1 bản active mỗi
    ///    Code, nhưng dữ-liệu cũ/tay có thể còn 2 — lấy bản MỚI nhất thay vì "bản nào cũng được".
    public static async Task<Guid?> ResolveDefaultWorkflowAsync(
        IApplicationDbContext db, ContractType contractType, CancellationToken ct)
    {
        var code = ContractApprovalWorkflowFamily.CodeFor(contractType);
        if (code is null) return null;

        return await db.ApprovalWorkflows.AsNoTracking()
            .Where(w => w.Code == code
                        && w.ApplicableType == ApprovalWorkflowApplicableType.Contract
                        && w.IsActive
                        && w.IsUserSelectable)
            .OrderByDescending(w => w.Version)
            .Select(w => (Guid?)w.Id)
            .FirstOrDefaultAsync(ct);
    }

    /// Rào CỬA TRÌNH (site 4/4) — mirror vai trò của rào nhóm site 3/3 bên KHKK
    /// (`ContractSigningPlanWorkflowService.SubmitAsync` gọi `EnsureWorkflowGroupMatchAsync`):
    /// đây là cửa DUY NHẤT bắt được HĐ pin lệch có TỪ TRƯỚC (3 cửa ghi chỉ chặn lúc GHI, nên HĐ tạo
    /// trước khi có rào — hoặc sau khi admin đổi mã quy trình — vẫn nằm sẵn trong DB với loại lệch).
    ///
    /// 🔴 CỐ Ý NHẸ HƠN cửa ghi — KHÔNG hỏi `IsActive` / `IsUserSelectable`:
    ///    admin tạo version mới là tự động hạ `IsActive` của bản cũ (`CreateAwDefinitionCommandHandler`),
    ///    và "gỡ ghim" là thao tác về khả-năng-CHỌN chứ không phải về hiệu-lực. Hỏi 2 cờ đó ở đây =
    ///    HĐ nháp đang chạy bỗng không trình được vì một thao tác Designer chẳng liên quan
    ///    ⇒ đúng thứ đề bài cấm ("KHÔNG chặn đường duyệt phiếu cũ đang chạy").
    ///    Chỉ giữ 2 vế về ĐỊNH DANH (còn tồn tại + đúng loại), vì pin sai định danh thì phiếu treo
    ///    im lặng trong hộp thư của không ai cả.
    public static async Task EnsurePinnedWorkflowStillValidAsync(
        IApplicationDbContext db, Guid workflowId, ContractType contractType, CancellationToken ct)
    {
        var aw = await db.ApprovalWorkflows.AsNoTracking()
            .Where(w => w.Id == workflowId)
            .Select(w => new { w.Code, w.ApplicableType })
            .FirstOrDefaultAsync(ct);

        if (aw is null)
            throw new ConflictException(
                "Quy trình duyệt đã pin cho HĐ này không còn tồn tại — vào HĐ (Nháp) chọn lại quy trình trước khi trình.");

        if (aw.ApplicableType != ApprovalWorkflowApplicableType.Contract)
            throw new ConflictException(
                $"Quy trình '{aw.Code}' đang pin áp dụng cho {aw.ApplicableType}, không khớp với Hợp đồng " +
                "— vào HĐ (Nháp) chọn lại quy trình đúng loại.");

        EnsureFamilyLock(aw.Code, contractType);
    }

    /// Thân của KHOÁ CỨNG (86) — dùng chung cho cửa ghi và cửa trình để 2 nơi không thể đi lệch.
    private static void EnsureFamilyLock(string? workflowCode, ContractType contractType)
    {
        var code = workflowCode ?? string.Empty;

        // Ngoài không-gian-tên họ ⇒ cho qua (xem chú thích đầu class).
        if (!code.StartsWith(ContractApprovalWorkflowFamily.CodePrefix, StringComparison.OrdinalIgnoreCase))
            return;

        var expected = ContractApprovalWorkflowFamily.CodeFor(contractType);

        // `expected == null` = loại HĐ nằm ngoài họ (enum nở thêm mà chưa bổ sung `Members`).
        // Không có mã kỳ vọng để đối chiếu ⇒ KHÔNG cho mượn mã họ của loại khác.
        if (expected is null)
            throw new ConflictException(
                $"Loại HĐ '{contractType}' chưa có quy trình riêng trong họ '{ContractApprovalWorkflowFamily.CodePrefix}*' " +
                $"— không gắn được quy trình '{code}' cho loại này.");

        if (string.Equals(code, expected, StringComparison.OrdinalIgnoreCase)) return;

        var shortName = ShortNameOf(contractType);
        throw new ConflictException(
            $"Quy trình '{code}' là quy trình duyệt của loại Hợp đồng KHÁC — HĐ loại \"{shortName}\" phải dùng " +
            $"quy trình '{expected}'. Không gắn chéo quy trình giữa các loại HĐ.");
    }

    /// Tên loại tiếng Việt lấy TỪ nhà 7 mã (không gõ lại chuỗi ở đây — mã đổi thì message đổi theo).
    private static string ShortNameOf(ContractType contractType)
    {
        foreach (var m in ContractApprovalWorkflowFamily.Members)
            if (m.Type == contractType) return m.ShortName;
        return contractType.ToString();
    }
}
