using CashLineERP.Domain.Common;
using CashLineERP.SharedKernel.Finance;
using CashLineERP.SharedKernel.Workflow;

namespace CashLineERP.Domain.Finance;

public sealed class PaymentChannelConfiguration : Entity
{
    public long Id { get; private set; }
    public string ChannelCode { get; private set; } = string.Empty;
    public string ChannelName { get; private set; } = string.Empty;
    public long? BranchId { get; private set; }
    public string IntegrationProviderCode { get; private set; } = string.Empty;
    public string ChannelTypeCode { get; private set; } = string.Empty;
    public string UsageCode { get; private set; } = string.Empty;
    public string CurrencyCode { get; private set; } = string.Empty;
    public long OutboundIntegrationEndpointId { get; private set; }
    public long? InboundIntegrationEndpointId { get; private set; }
    public bool SupportsCardTokenReferences { get; private set; }
    public bool UsesWalletRuleEngine { get; private set; }
    public bool RequiresCustomerReference { get; private set; }
    public decimal? MinimumAmount { get; private set; }
    public decimal? MaximumAmount { get; private set; }
    public int SettlementLeadDays { get; private set; }
    public long StatusId { get; private set; } = WorkflowStatusSeedIds.Draft;
    public bool IsActive { get; private set; }
    public string? Notes { get; private set; }
    public DateTime? SubmittedAtUtc { get; private set; }
    public long? SubmittedByUserId { get; private set; }
    public DateTime? ApprovedAtUtc { get; private set; }
    public long? ApprovedByUserId { get; private set; }
    public DateTime? ActivatedAtUtc { get; private set; }
    public long? ActivatedByUserId { get; private set; }

    public PaymentChannelConfiguration(
        string channelCode,
        string channelName,
        long? branchId,
        string integrationProviderCode,
        string channelTypeCode,
        string usageCode,
        string currencyCode,
        long outboundIntegrationEndpointId,
        long? inboundIntegrationEndpointId,
        bool supportsCardTokenReferences,
        bool usesWalletRuleEngine,
        bool requiresCustomerReference,
        decimal? minimumAmount,
        decimal? maximumAmount,
        int settlementLeadDays,
        string? notes)
    {
        if (string.IsNullOrWhiteSpace(channelCode))
        {
            throw new InvalidOperationException("Payment channel code is required.");
        }

        if (string.IsNullOrWhiteSpace(channelName))
        {
            throw new InvalidOperationException("Payment channel name is required.");
        }

        if (branchId.HasValue && branchId.Value <= 0)
        {
            throw new InvalidOperationException("Payment channel branch must be a valid positive identifier when provided.");
        }

        if (outboundIntegrationEndpointId <= 0)
        {
            throw new InvalidOperationException("Payment channel requires a valid outbound integration endpoint.");
        }

        if (inboundIntegrationEndpointId.HasValue && inboundIntegrationEndpointId.Value <= 0)
        {
            throw new InvalidOperationException("Payment channel inbound integration endpoint must be a valid positive identifier when provided.");
        }

        if (minimumAmount.HasValue && minimumAmount.Value < 0m)
        {
            throw new InvalidOperationException("Payment channel minimum amount cannot be negative.");
        }

        if (maximumAmount.HasValue && maximumAmount.Value <= 0m)
        {
            throw new InvalidOperationException("Payment channel maximum amount must be greater than zero.");
        }

        if (minimumAmount.HasValue && maximumAmount.HasValue && minimumAmount.Value > maximumAmount.Value)
        {
            throw new InvalidOperationException("Payment channel minimum amount cannot exceed maximum amount.");
        }

        if (settlementLeadDays is < 0 or > 30)
        {
            throw new InvalidOperationException("Payment channel settlement lead days must be between 0 and 30.");
        }

        ChannelCode = channelCode.Trim().ToUpperInvariant();
        ChannelName = channelName.Trim();
        BranchId = branchId;
        IntegrationProviderCode = PaymentChannelCatalog.NormalizeIntegrationProviderCode(integrationProviderCode);
        ChannelTypeCode = PaymentChannelCatalog.NormalizeChannelTypeCode(channelTypeCode);
        UsageCode = PaymentChannelCatalog.NormalizeUsageCode(usageCode);
        CurrencyCode = PaymentChannelCatalog.NormalizeCurrencyCode(currencyCode);
        OutboundIntegrationEndpointId = outboundIntegrationEndpointId;
        InboundIntegrationEndpointId = inboundIntegrationEndpointId;
        SupportsCardTokenReferences = supportsCardTokenReferences;
        UsesWalletRuleEngine = usesWalletRuleEngine;
        RequiresCustomerReference = requiresCustomerReference;
        MinimumAmount = minimumAmount;
        MaximumAmount = maximumAmount;
        SettlementLeadDays = settlementLeadDays;
        Notes = string.IsNullOrWhiteSpace(notes) ? null : notes.Trim();

        ValidateFeatureAlignment();
    }

    private PaymentChannelConfiguration()
    {
    }

    public void Submit(long actorUserId)
    {
        if (StatusId != WorkflowStatusSeedIds.Draft && StatusId != WorkflowStatusSeedIds.Returned)
        {
            throw new InvalidOperationException("Only draft or returned payment channels can be submitted.");
        }

        StatusId = WorkflowStatusSeedIds.Submitted;
        SubmittedAtUtc = DateTime.UtcNow;
        SubmittedByUserId = actorUserId;
        ApprovedAtUtc = null;
        ApprovedByUserId = null;
        Touch(actorUserId);
    }

    public void Approve(long actorUserId)
    {
        if (StatusId == WorkflowStatusSeedIds.Approved)
        {
            return;
        }

        if (StatusId != WorkflowStatusSeedIds.Submitted)
        {
            throw new InvalidOperationException("Only submitted payment channels can be approved.");
        }

        if (SubmittedByUserId == actorUserId)
        {
            throw new InvalidOperationException("Payment channel maker-checker control prohibits self-approval.");
        }

        StatusId = WorkflowStatusSeedIds.Approved;
        ApprovedAtUtc = DateTime.UtcNow;
        ApprovedByUserId = actorUserId;
        Touch(actorUserId);
    }

    public void Activate(long actorUserId)
    {
        if (StatusId != WorkflowStatusSeedIds.Approved)
        {
            throw new InvalidOperationException("Only approved payment channels can be activated.");
        }

        ValidateFeatureAlignment();

        IsActive = true;
        ActivatedAtUtc = DateTime.UtcNow;
        ActivatedByUserId = actorUserId;
        Touch(actorUserId);
    }

    private void ValidateFeatureAlignment()
    {
        if (string.Equals(ChannelTypeCode, "CARD_TOKEN", StringComparison.OrdinalIgnoreCase) && !SupportsCardTokenReferences)
        {
            throw new InvalidOperationException("Card-token payment channels must support card token references.");
        }

        if (string.Equals(ChannelTypeCode, "MOBILE_WALLET", StringComparison.OrdinalIgnoreCase) && !UsesWalletRuleEngine)
        {
            throw new InvalidOperationException("Mobile-wallet payment channels must use the wallet rule engine in the current baseline.");
        }

        if (!string.Equals(ChannelTypeCode, "CARD_TOKEN", StringComparison.OrdinalIgnoreCase) && SupportsCardTokenReferences)
        {
            throw new InvalidOperationException("Only card-token payment channels can enable card token reference support.");
        }

        if (!string.Equals(ChannelTypeCode, "MOBILE_WALLET", StringComparison.OrdinalIgnoreCase) && UsesWalletRuleEngine)
        {
            throw new InvalidOperationException("Only mobile-wallet payment channels can enable wallet rule engine support.");
        }
    }
}
