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

namespace CashLineERP.Application.Customers;

public sealed class CustomerProfileService : ICustomerProfileService
{
    private readonly ICustomerProfileRepository _repository;
    private readonly IOnboardingAccessProvisioningService _onboardingAccessProvisioningService;
    private readonly IOnboardingReviewApprovalGateService _onboardingReviewApprovalGateService;

    public CustomerProfileService(ICustomerProfileRepository repository)
        : this(repository, NoOpOnboardingAccessProvisioningService.Instance, NoOpOnboardingReviewApprovalGateService.Instance)
    {
    }

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

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

    public CustomerProfileResponse? GetCustomerProfile(long customerProfileId)
    {
        var profile = _repository.GetCustomerProfile(customerProfileId);
        return profile is null ? null : Map(profile);
    }

    public CustomerProfileResponse CreateCustomerProfile(CreateCustomerProfileRequest request) =>
        Map(_repository.Create(new CreateCustomerProfileCommand(
            request.BusinessPartyId,
            request.FirmNationalId,
            request.CustomerClassificationCode,
            request.CustomerSegmentCode,
            request.DefaultCurrencyCode,
            request.SalesChannelCode,
            request.PriceTierCode,
            request.InvoiceDeliveryMethodCode,
            request.Notes)));

    public CustomerProfileResponse SubmitCustomerProfile(long customerProfileId, long actorUserId) =>
        Map(_repository.Submit(customerProfileId, actorUserId));

    public CustomerProfileResponse ApproveCustomerProfile(long customerProfileId, long actorUserId)
    {
        _onboardingReviewApprovalGateService.EnsureApprovalAllowed("CUSTOMER", customerProfileId);
        return Map(_repository.Approve(customerProfileId, actorUserId));
    }

    public CustomerProfileResponse ReturnCustomerProfile(long customerProfileId, long actorUserId, ReviewCustomerProfileRequest request) =>
        Map(_repository.Return(customerProfileId, actorUserId, request.ReviewNotes));

    public CustomerProfileResponse RejectCustomerProfile(long customerProfileId, long actorUserId, ReviewCustomerProfileRequest request) =>
        Map(_repository.Reject(customerProfileId, actorUserId, request.ReviewNotes));

    public CustomerProfileResponse HoldCustomerProfile(long customerProfileId, long actorUserId, HoldCustomerProfileRequest request) =>
        Map(_repository.Hold(customerProfileId, actorUserId, request.HoldReason));

    public CustomerProfileResponse ReopenCustomerProfile(long customerProfileId, long actorUserId, ReopenCustomerProfileRequest request) =>
        Map(_repository.Reopen(customerProfileId, actorUserId, request.ReopenReason));

    public CustomerProfileResponse ActivateCustomerProfile(long customerProfileId, long actorUserId)
    {
        var activated = _repository.Activate(customerProfileId, actorUserId);
        _onboardingAccessProvisioningService.ProvisionCustomerAccess(activated.BusinessPartyId, actorUserId);
        return Map(activated);
    }

    private static CustomerProfileResponse Map(CustomerProfileDetailView profile) =>
        new(
            profile.CustomerProfileId,
            profile.CustomerProfileCode,
            profile.BusinessPartyId,
            profile.PartyCode,
            profile.PartyName,
            profile.CountryCode,
            profile.TaxRegistrationNumber,
            profile.CreditLimitAmount,
            profile.CreditTermsDays,
            profile.IsCreditHoldEnabled,
            profile.CreditHoldReason,
            profile.CustomerClassificationCode,
            profile.CustomerSegmentCode,
            profile.DefaultCurrencyCode,
            profile.SalesChannelCode,
            profile.PriceTierCode,
            profile.InvoiceDeliveryMethodCode,
            profile.TaxProfileStatusCode,
            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)
        {
        }
    }
}
