using CashLineERP.Application.Customers;
using CashLineERP.Application.Parties;
using CashLineERP.Application.Suppliers;
using CashLineERP.Contracts.Customers;
using CashLineERP.Contracts.External;
using CashLineERP.Contracts.Parties;
using CashLineERP.Contracts.Suppliers;

namespace CashLineERP.Application.External;

public sealed class PublicOnboardingService : IPublicOnboardingService
{
    private const long PublicSubmissionActorUserId = 0;

    private readonly IPartyOnboardingService _partyOnboardingService;
    private readonly ISupplierProfileService _supplierProfileService;
    private readonly ICustomerProfileService _customerProfileService;
    private readonly IOnboardingLeadService _onboardingLeadService;

    public PublicOnboardingService(
        IPartyOnboardingService partyOnboardingService,
        ISupplierProfileService supplierProfileService,
        ICustomerProfileService customerProfileService,
        IOnboardingLeadService onboardingLeadService)
    {
        _partyOnboardingService = partyOnboardingService;
        _supplierProfileService = supplierProfileService;
        _customerProfileService = customerProfileService;
        _onboardingLeadService = onboardingLeadService;
    }

    public PublicOnboardingLeadResponse CreateLead(CreatePublicOnboardingLeadRequest request) =>
        _onboardingLeadService.CreateLead(request);

    public PublicOnboardingLeadResponse? GetLead(string leadCode) =>
        _onboardingLeadService.GetPublicLead(leadCode);

    public PublicSupplierOnboardingApplicationResponse CreateSupplierApplication(CreatePublicSupplierOnboardingApplicationRequest request)
    {
        EnsureCommunicationDestination(request.Email, request.MobileNumber);
        MarkLeadAsStarted(request.LeadCode);

        var party = _partyOnboardingService.CreateParty(new CreateBusinessPartyRequest(
            GeneratePartyCode("SUP"),
            request.PartyName,
            "SUPPLIER",
            request.CountryCode,
            request.FirmNationalId,
            request.TaxRegistrationNumber,
            request.TaxBranchCode,
            request.Email,
            request.MobileNumber,
            request.AddressLine1,
            null,
            null,
            false,
            null));

        RecordComplianceDocuments(party.PartyId, request.ComplianceDocuments);

        var profile = _supplierProfileService.CreateSupplierProfile(new CreateSupplierProfileRequest(
            party.PartyId,
            request.FirmNationalId,
            request.SupplierCategoryCode,
            request.DefaultCurrencyCode,
            request.PreferredPaymentMethodCode,
            request.PaymentTermsDays,
            request.LeadTimeDays,
            request.MinimumOrderAmount,
            request.BankName,
            request.BankAccountNumber,
            request.BankSwiftCode,
            request.Notes));

        var submitted = _supplierProfileService.SubmitSupplierProfile(profile.SupplierProfileId, PublicSubmissionActorUserId);
        MarkLeadAsSubmitted(request.LeadCode, party.PartyId, submitted.SupplierProfileId);

        return new PublicSupplierOnboardingApplicationResponse(
            party.PartyId,
            party.PartyCode,
            party.PartyName,
            submitted.SupplierProfileId,
            submitted.SupplierProfileCode,
            submitted.StatusCode,
            submitted.ComplianceStatusCode,
            submitted.ComplianceDocumentCount,
            "Supplier onboarding application submitted for operations review.");
    }

    public PublicCustomerOnboardingApplicationResponse CreateCustomerApplication(CreatePublicCustomerOnboardingApplicationRequest request)
    {
        EnsureCommunicationDestination(request.Email, request.MobileNumber);
        MarkLeadAsStarted(request.LeadCode);

        var party = _partyOnboardingService.CreateParty(new CreateBusinessPartyRequest(
            GeneratePartyCode("CUS"),
            request.PartyName,
            "CUSTOMER",
            request.CountryCode,
            request.FirmNationalId,
            request.TaxRegistrationNumber,
            request.TaxBranchCode,
            request.Email,
            request.MobileNumber,
            request.AddressLine1,
            request.CreditLimitAmount,
            request.CreditTermsDays,
            request.IsCreditHoldEnabled,
            request.CreditHoldReason));

        RecordComplianceDocuments(party.PartyId, request.ComplianceDocuments);

        var profile = _customerProfileService.CreateCustomerProfile(new CreateCustomerProfileRequest(
            party.PartyId,
            request.FirmNationalId,
            request.CustomerClassificationCode,
            request.CustomerSegmentCode,
            request.DefaultCurrencyCode,
            request.SalesChannelCode,
            request.PriceTierCode,
            request.InvoiceDeliveryMethodCode,
            request.Notes));

        var submitted = _customerProfileService.SubmitCustomerProfile(profile.CustomerProfileId, PublicSubmissionActorUserId);
        MarkLeadAsSubmitted(request.LeadCode, party.PartyId, submitted.CustomerProfileId);

        return new PublicCustomerOnboardingApplicationResponse(
            party.PartyId,
            party.PartyCode,
            party.PartyName,
            submitted.CustomerProfileId,
            submitted.CustomerProfileCode,
            submitted.StatusCode,
            submitted.TaxProfileStatusCode,
            submitted.ComplianceDocumentCount,
            "Customer onboarding application submitted for operations review.");
    }

    private void RecordComplianceDocuments(
        long partyId,
        IReadOnlyCollection<RecordBusinessPartyComplianceDocumentRequest>? complianceDocuments)
    {
        if (complianceDocuments is null)
        {
            return;
        }

        foreach (var document in complianceDocuments)
        {
            _partyOnboardingService.RecordComplianceDocument(
                PublicSubmissionActorUserId,
                partyId,
                document);
        }
    }

    private static void EnsureCommunicationDestination(string? email, string? mobileNumber)
    {
        if (string.IsNullOrWhiteSpace(email) && string.IsNullOrWhiteSpace(mobileNumber))
        {
            throw new InvalidOperationException("Public onboarding requires at least one communication destination, email or mobile number.");
        }
    }

    private static string GeneratePartyCode(string prefix)
    {
        var suffix = $"{DateTime.UtcNow:yyMMddHHmmss}{Guid.NewGuid():N}"[..12];
        return $"{prefix}-PUB-{suffix}";
    }

    private void MarkLeadAsStarted(string? leadCode)
    {
        if (string.IsNullOrWhiteSpace(leadCode))
        {
            return;
        }

        _onboardingLeadService.MarkSelfServiceStarted(leadCode);
    }

    private void MarkLeadAsSubmitted(string? leadCode, long businessPartyId, long subjectProfileId)
    {
        if (string.IsNullOrWhiteSpace(leadCode))
        {
            return;
        }

        _onboardingLeadService.MarkSubmitted(leadCode, businessPartyId, subjectProfileId);
    }
}
