using CashLineERP.Contracts.Suppliers;
using CashLineERP.Application.Identity;
using CashLineERP.Application.Parties;

namespace CashLineERP.Application.Suppliers;

public sealed class SupplierProfileService : ISupplierProfileService
{
    private readonly ISupplierProfileRepository _repository;
    private readonly IOnboardingAccessProvisioningService _onboardingAccessProvisioningService;
    private readonly IOnboardingReviewApprovalGateService _onboardingReviewApprovalGateService;

    public SupplierProfileService(ISupplierProfileRepository repository)
        : this(repository, NoOpOnboardingAccessProvisioningService.Instance, NoOpOnboardingReviewApprovalGateService.Instance)
    {
    }

    public SupplierProfileService(
        ISupplierProfileRepository repository,
        IOnboardingAccessProvisioningService onboardingAccessProvisioningService,
        IOnboardingReviewApprovalGateService onboardingReviewApprovalGateService)
    {
        _repository = repository;
        _onboardingAccessProvisioningService = onboardingAccessProvisioningService;
        _onboardingReviewApprovalGateService = onboardingReviewApprovalGateService;
    }

    public IReadOnlyCollection<SupplierProfileResponse> GetSupplierProfiles(bool activeOnly = false, string? statusCode = null, long? businessPartyId = null) =>
        _repository.GetSupplierProfiles(activeOnly, statusCode, businessPartyId)
            .Select(Map)
            .ToArray();

    public SupplierProfileResponse? GetSupplierProfile(long supplierProfileId)
    {
        var profile = _repository.GetSupplierProfile(supplierProfileId);
        return profile is null ? null : Map(profile);
    }

    public SupplierProfileResponse CreateSupplierProfile(CreateSupplierProfileRequest request) =>
        Map(_repository.Create(new CreateSupplierProfileCommand(
            request.BusinessPartyId,
            request.FirmNationalId,
            request.SupplierCategoryCode,
            request.DefaultCurrencyCode,
            request.PreferredPaymentMethodCode,
            request.PaymentTermsDays,
            request.LeadTimeDays,
            request.MinimumOrderAmount,
            request.BankName,
            request.BankAccountNumber,
            request.BankSwiftCode,
            request.Notes)));

    public SupplierProfileResponse SubmitSupplierProfile(long supplierProfileId, long actorUserId) =>
        Map(_repository.Submit(supplierProfileId, actorUserId));

    public SupplierProfileResponse ApproveSupplierProfile(long supplierProfileId, long actorUserId)
    {
        _onboardingReviewApprovalGateService.EnsureApprovalAllowed("SUPPLIER", supplierProfileId);
        return Map(_repository.Approve(supplierProfileId, actorUserId));
    }

    public SupplierProfileResponse ReturnSupplierProfile(long supplierProfileId, long actorUserId, ReviewSupplierProfileRequest request) =>
        Map(_repository.Return(supplierProfileId, actorUserId, request.ReviewNotes));

    public SupplierProfileResponse RejectSupplierProfile(long supplierProfileId, long actorUserId, ReviewSupplierProfileRequest request) =>
        Map(_repository.Reject(supplierProfileId, actorUserId, request.ReviewNotes));

    public SupplierProfileResponse HoldSupplierProfile(long supplierProfileId, long actorUserId, HoldSupplierProfileRequest request) =>
        Map(_repository.Hold(supplierProfileId, actorUserId, request.HoldReason));

    public SupplierProfileResponse ReopenSupplierProfile(long supplierProfileId, long actorUserId, ReopenSupplierProfileRequest request) =>
        Map(_repository.Reopen(supplierProfileId, actorUserId, request.ReopenReason));

    public SupplierProfileResponse ActivateSupplierProfile(long supplierProfileId, long actorUserId)
    {
        var activated = _repository.Activate(supplierProfileId, actorUserId);
        _onboardingAccessProvisioningService.ProvisionSupplierAccess(activated.BusinessPartyId, actorUserId);
        return Map(activated);
    }

    public SupplierProfileResponse VerifySupplierBank(long supplierProfileId, long actorUserId, VerifySupplierBankRequest request) =>
        Map(_repository.VerifyBank(supplierProfileId, actorUserId, request.VerificationReference));

    public SupplierProfileResponse RecordPerformanceReview(long supplierProfileId, long actorUserId, RecordSupplierPerformanceReviewRequest request) =>
        Map(_repository.RecordPerformanceReview(
            supplierProfileId,
            actorUserId,
            request.RiskRatingCode,
            request.PerformanceScore,
            request.OnTimeDeliveryScore,
            request.QualityScore,
            request.PerformanceNotes));

    private static SupplierProfileResponse Map(SupplierProfileDetailView profile) =>
        new(
            profile.SupplierProfileId,
            profile.SupplierProfileCode,
            profile.BusinessPartyId,
            profile.PartyCode,
            profile.PartyName,
            profile.CountryCode,
            profile.TaxRegistrationNumber,
            profile.SupplierCategoryCode,
            profile.DefaultCurrencyCode,
            profile.PreferredPaymentMethodCode,
            profile.PaymentTermsDays,
            profile.LeadTimeDays,
            profile.MinimumOrderAmount,
            profile.BankName,
            profile.BankAccountNumber,
            profile.BankSwiftCode,
            profile.BankVerificationStatusCode,
            profile.BankVerifiedAtUtc,
            profile.BankVerifiedByUserId,
            profile.BankVerificationReference,
            profile.RiskRatingCode,
            profile.PerformanceScore,
            profile.OnTimeDeliveryScore,
            profile.QualityScore,
            profile.LastPerformanceReviewAtUtc,
            profile.LastPerformanceReviewedByUserId,
            profile.PerformanceNotes,
            profile.ComplianceStatusCode,
            profile.ComplianceDocumentCount,
            profile.StatusCode,
            profile.IsActive,
            profile.IsOnHold,
            profile.HoldReason,
            profile.ReviewDecisionNotes,
            profile.Notes,
            profile.SubmittedAtUtc,
            profile.SubmittedByUserId,
            profile.ApprovedAtUtc,
            profile.ApprovedByUserId,
            profile.ActivatedAtUtc,
            profile.ActivatedByUserId);

    private sealed class NoOpOnboardingAccessProvisioningService : IOnboardingAccessProvisioningService
    {
        public static readonly NoOpOnboardingAccessProvisioningService Instance = new();

        public void ProvisionSupplierAccess(long partyId, long actorUserId)
        {
        }

        public void ProvisionCustomerAccess(long partyId, long actorUserId)
        {
        }
    }

    private sealed class NoOpOnboardingReviewApprovalGateService : IOnboardingReviewApprovalGateService
    {
        public static readonly NoOpOnboardingReviewApprovalGateService Instance = new();

        public void EnsureApprovalAllowed(string subjectTypeCode, long subjectProfileId)
        {
        }
    }
}
