using CashLineERP.Contracts.External;
using CashLineERP.Contracts.Parties;
using CashLineERP.Domain.Parties;

namespace CashLineERP.Application.Parties;

public sealed class OnboardingLeadService : IOnboardingLeadService
{
    private readonly IOnboardingLeadRepository _repository;

    public OnboardingLeadService(IOnboardingLeadRepository repository)
    {
        _repository = repository;
    }

    public PublicOnboardingLeadResponse CreateLead(CreatePublicOnboardingLeadRequest request)
    {
        EnsureCommunicationDestination(request.Email, request.MobileNumber);
        var lead = _repository.Create(new OnboardingLead(
            GenerateLeadCode(request.ProspectTypeCode),
            request.ProspectTypeCode,
            request.CompanyName,
            request.ContactName,
            request.Email,
            request.MobileNumber,
            "PUBLIC_PORTAL",
            request.Notes));

        return MapPublic(lead);
    }

    public PublicOnboardingLeadResponse? GetPublicLead(string leadCode)
    {
        var lead = _repository.GetByLeadCode(leadCode);
        return lead is null ? null : MapPublic(lead);
    }

    public IReadOnlyCollection<OnboardingLeadSummaryResponse> GetLeads(string? prospectTypeCode = null, string? statusCode = null) =>
        _repository.GetLeads(prospectTypeCode, statusCode)
            .Select(MapSummary)
            .ToArray();

    public void MarkSelfServiceStarted(string leadCode)
    {
        var lead = GetRequiredLead(leadCode);
        lead.MarkSelfServiceStarted();
        _repository.Update(lead);
    }

    public void MarkSubmitted(string leadCode, long businessPartyId, long subjectProfileId)
    {
        var lead = GetRequiredLead(leadCode);
        lead.MarkSubmitted(businessPartyId, subjectProfileId);
        _repository.Update(lead);
    }

    private OnboardingLead GetRequiredLead(string leadCode) =>
        _repository.GetByLeadCode(leadCode)
        ?? throw new InvalidOperationException($"Onboarding lead '{leadCode}' was not found.");

    private static PublicOnboardingLeadResponse MapPublic(OnboardingLead lead) =>
        new(
            lead.Id,
            lead.LeadCode,
            lead.ProspectTypeCode,
            lead.CompanyName,
            lead.ContactName,
            lead.Email,
            lead.MobileNumber,
            lead.StatusCode,
            lead.SourceChannelCode,
            lead.CreatedAtUtc,
            lead.SubmittedAtUtc);

    private static OnboardingLeadSummaryResponse MapSummary(OnboardingLead lead) =>
        new(
            lead.Id,
            lead.LeadCode,
            lead.ProspectTypeCode,
            lead.CompanyName,
            lead.ContactName,
            lead.Email,
            lead.MobileNumber,
            lead.StatusCode,
            lead.SourceChannelCode,
            lead.BusinessPartyId,
            lead.SubjectProfileId,
            lead.CreatedAtUtc,
            lead.SubmittedAtUtc);

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

    private static string GenerateLeadCode(string prospectTypeCode)
    {
        var normalizedType = string.IsNullOrWhiteSpace(prospectTypeCode) ? "GEN" : prospectTypeCode.Trim().ToUpperInvariant();
        var prefix = normalizedType.StartsWith("SUP") ? "SUP" : normalizedType.StartsWith("CUS") ? "CUS" : "GEN";
        var suffix = $"{DateTime.UtcNow:yyMMddHHmmss}{Guid.NewGuid():N}"[..12];
        return $"{prefix}-LEAD-{suffix}";
    }
}
