using FluentValidation;
using MediatR;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using SolutionErp.Application.Common.Exceptions;
using SolutionErp.Application.Common.Interfaces;
using SolutionErp.Domain.Contracts;   // ChangelogAction
using SolutionErp.Domain.Identity;
using SolutionErp.Domain.PurchaseEvaluations;

namespace SolutionErp.Application.PurchaseEvaluations;

// Ý kiến 4 phòng ban (Phê duyệt / CCM / MuaHàng / SM-PM) trên PHIẾU TRÌNH KÝ.
// Upsert pattern — UPDATE in-place khi user đổi ý (không version), audit qua
// PurchaseEvaluationChangelog. UNIQUE index (PEId, Kind) bảo vệ tối đa 1 row
// mỗi loại phòng ban per phiếu.

// ========== UPSERT (Add nếu chưa có, Update nếu rồi) ==========

public record UpsertPeDepartmentOpinionCommand(
    Guid PurchaseEvaluationId,
    PeDepartmentKind Kind,
    string? Opinion,
    bool Sign) : IRequest<Guid>;
// Sign=true → set SignedAt + UserId hiện tại (đóng dấu xác nhận).
// Sign=false → chỉ lưu text Opinion (chưa ký).

public class UpsertPeDepartmentOpinionCommandValidator : AbstractValidator<UpsertPeDepartmentOpinionCommand>
{
    public UpsertPeDepartmentOpinionCommandValidator()
    {
        RuleFor(x => x.PurchaseEvaluationId).NotEmpty();
        RuleFor(x => x.Kind).IsInEnum();
        RuleFor(x => x.Opinion).MaximumLength(2000);
    }
}

public class UpsertPeDepartmentOpinionCommandHandler(
    IApplicationDbContext db,
    ICurrentUser currentUser,
    UserManager<User> userManager) : IRequestHandler<UpsertPeDepartmentOpinionCommand, Guid>
{
    public async Task<Guid> Handle(UpsertPeDepartmentOpinionCommand request, CancellationToken ct)
    {
        if (!currentUser.IsAuthenticated || currentUser.UserId is null)
            throw new UnauthorizedException();

        var pe = await db.PurchaseEvaluations.FirstOrDefaultAsync(p => p.Id == request.PurchaseEvaluationId, ct)
            ?? throw new NotFoundException("PurchaseEvaluation", request.PurchaseEvaluationId);

        var existing = await db.PurchaseEvaluationDepartmentOpinions
            .FirstOrDefaultAsync(o => o.PurchaseEvaluationId == pe.Id && o.Kind == request.Kind, ct);

        string? actorName = null;
        if (request.Sign)
        {
            var u = await userManager.FindByIdAsync(currentUser.UserId.Value.ToString());
            actorName = u?.FullName ?? u?.Email;
        }

        Guid resultId;
        ChangelogAction action;
        if (existing == null)
        {
            var entity = new PurchaseEvaluationDepartmentOpinion
            {
                PurchaseEvaluationId = pe.Id,
                Kind = request.Kind,
                Opinion = request.Opinion,
                SignedAt = request.Sign ? DateTime.UtcNow : null,
                UserId = request.Sign ? currentUser.UserId : null,
                UserName = request.Sign ? actorName : null,
            };
            db.PurchaseEvaluationDepartmentOpinions.Add(entity);
            resultId = entity.Id;
            action = ChangelogAction.Insert;
        }
        else
        {
            existing.Opinion = request.Opinion;
            if (request.Sign)
            {
                existing.SignedAt = DateTime.UtcNow;
                existing.UserId = currentUser.UserId;
                existing.UserName = actorName;
            }
            // Sign=false giữ nguyên SignedAt/UserId cũ (user đã ký rồi vẫn giữ chữ ký).
            resultId = existing.Id;
            action = ChangelogAction.Update;
        }

        db.PurchaseEvaluationChangelogs.Add(new PurchaseEvaluationChangelog
        {
            PurchaseEvaluationId = pe.Id,
            EntityType = PurchaseEvaluationEntityType.Header,   // không có entity type riêng cho opinion
            EntityId = resultId,
            Action = action,
            PhaseAtChange = pe.Phase,
            UserId = currentUser.UserId,
            UserName = actorName,
            Summary = request.Sign
                ? $"Ý kiến {KindLabel(request.Kind)} — đã ký"
                : $"Ý kiến {KindLabel(request.Kind)} — cập nhật text",
        });

        await db.SaveChangesAsync(ct);
        return resultId;
    }

    private static string KindLabel(PeDepartmentKind k) => k switch
    {
        PeDepartmentKind.PheDuyet => "Phê duyệt",
        PeDepartmentKind.Ccm => "P.CCM",
        PeDepartmentKind.MuaHang => "P.Mua hàng",
        PeDepartmentKind.SmPm => "SM-PM",
        _ => k.ToString(),
    };
}

// ========== DELETE (rare — admin override) ==========

public record DeletePeDepartmentOpinionCommand(Guid PurchaseEvaluationId, PeDepartmentKind Kind) : IRequest;

public class DeletePeDepartmentOpinionCommandHandler(
    IApplicationDbContext db,
    ICurrentUser currentUser) : IRequestHandler<DeletePeDepartmentOpinionCommand>
{
    public async Task Handle(DeletePeDepartmentOpinionCommand request, CancellationToken ct)
    {
        var entity = await db.PurchaseEvaluationDepartmentOpinions
            .FirstOrDefaultAsync(o => o.PurchaseEvaluationId == request.PurchaseEvaluationId && o.Kind == request.Kind, ct)
            ?? throw new NotFoundException("PEDepartmentOpinion", request.Kind);

        var pe = await db.PurchaseEvaluations.FirstOrDefaultAsync(p => p.Id == request.PurchaseEvaluationId, ct);
        db.PurchaseEvaluationDepartmentOpinions.Remove(entity);

        if (pe is not null)
        {
            db.PurchaseEvaluationChangelogs.Add(new PurchaseEvaluationChangelog
            {
                PurchaseEvaluationId = pe.Id,
                EntityType = PurchaseEvaluationEntityType.Header,
                Action = ChangelogAction.Delete,
                PhaseAtChange = pe.Phase,
                UserId = currentUser.UserId,
                Summary = $"Xóa ý kiến phòng ban ({request.Kind})",
            });
        }

        await db.SaveChangesAsync(ct);
    }
}
