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

namespace CashLineERP.Domain.Customers;

public sealed class CustomerProfile : Entity
{
    public long Id { get; private set; }
    public string CustomerProfileCode { get; private set; } = string.Empty;
    public long BusinessPartyId { get; private set; }
    public string CustomerClassificationCode { get; private set; } = string.Empty;
    public string CustomerSegmentCode { get; private set; } = string.Empty;
    public string DefaultCurrencyCode { get; private set; } = string.Empty;
    public string SalesChannelCode { get; private set; } = string.Empty;
    public string PriceTierCode { get; private set; } = string.Empty;
    public string InvoiceDeliveryMethodCode { get; private set; } = string.Empty;
    public string TaxProfileStatusCode { get; private set; } = TaxProfileStatus.Pending;
    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 bool IsOnHold { get; private set; }
    public string? HoldReason { get; private set; }
    public DateTime? HeldAtUtc { get; private set; }
    public long? HeldByUserId { get; private set; }
    public string? ReviewDecisionNotes { get; private set; }
    public DateTime? ReopenedAtUtc { get; private set; }
    public long? ReopenedByUserId { get; private set; }
    public string? ReopenReason { get; private set; }

    public CustomerProfile(
        string customerProfileCode,
        long businessPartyId,
        string customerClassificationCode,
        string customerSegmentCode,
        string defaultCurrencyCode,
        string salesChannelCode,
        string priceTierCode,
        string invoiceDeliveryMethodCode,
        string? notes)
    {
        if (string.IsNullOrWhiteSpace(customerProfileCode))
        {
            throw new InvalidOperationException("Customer profile code is required.");
        }

        if (businessPartyId <= 0)
        {
            throw new InvalidOperationException("Customer profile requires a valid customer business party.");
        }

        CustomerClassificationCode = NormalizeClassificationCode(customerClassificationCode);

        if (string.IsNullOrWhiteSpace(customerSegmentCode))
        {
            throw new InvalidOperationException("Customer segment code is required.");
        }

        if (string.IsNullOrWhiteSpace(defaultCurrencyCode))
        {
            throw new InvalidOperationException("Customer default currency code is required.");
        }

        if (string.IsNullOrWhiteSpace(salesChannelCode))
        {
            throw new InvalidOperationException("Customer sales channel code is required.");
        }

        if (string.IsNullOrWhiteSpace(priceTierCode))
        {
            throw new InvalidOperationException("Customer price tier code is required.");
        }

        if (string.IsNullOrWhiteSpace(invoiceDeliveryMethodCode))
        {
            throw new InvalidOperationException("Customer invoice delivery method code is required.");
        }

        CustomerProfileCode = customerProfileCode.Trim().ToUpperInvariant();
        BusinessPartyId = businessPartyId;
        CustomerSegmentCode = customerSegmentCode.Trim().ToUpperInvariant();
        DefaultCurrencyCode = defaultCurrencyCode.Trim().ToUpperInvariant();
        SalesChannelCode = salesChannelCode.Trim().ToUpperInvariant();
        PriceTierCode = priceTierCode.Trim().ToUpperInvariant();
        InvoiceDeliveryMethodCode = invoiceDeliveryMethodCode.Trim().ToUpperInvariant();
        Notes = string.IsNullOrWhiteSpace(notes) ? null : notes.Trim();
    }

    private CustomerProfile()
    {
    }

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

        StatusId = WorkflowStatusSeedIds.Submitted;
        SubmittedAtUtc = DateTime.UtcNow;
        SubmittedByUserId = actorUserId;
        ApprovedAtUtc = null;
        ApprovedByUserId = null;
        ActivatedAtUtc = null;
        ActivatedByUserId = null;
        IsOnHold = false;
        HoldReason = null;
        HeldAtUtc = null;
        HeldByUserId = null;
        ReviewDecisionNotes = null;
        Touch(actorUserId);
    }

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

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

        if (IsOnHold)
        {
            throw new InvalidOperationException("Customer profile on hold cannot be approved.");
        }

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

        TaxProfileStatusCode = TaxProfileStatus.Approved;
        StatusId = WorkflowStatusSeedIds.Approved;
        ApprovedAtUtc = DateTime.UtcNow;
        ApprovedByUserId = actorUserId;
        ReviewDecisionNotes = null;
        Touch(actorUserId);
    }

    public void Return(long actorUserId, string reviewNotes)
    {
        if (StatusId != WorkflowStatusSeedIds.Submitted)
        {
            throw new InvalidOperationException("Only submitted customer profiles can be returned.");
        }

        StatusId = WorkflowStatusSeedIds.Returned;
        ReviewDecisionNotes = NormalizeReviewNotes(reviewNotes, "return");
        IsOnHold = false;
        HoldReason = null;
        HeldAtUtc = null;
        HeldByUserId = null;
        Touch(actorUserId);
    }

    public void Reject(long actorUserId, string reviewNotes)
    {
        if (StatusId != WorkflowStatusSeedIds.Submitted)
        {
            throw new InvalidOperationException("Only submitted customer profiles can be rejected.");
        }

        StatusId = WorkflowStatusSeedIds.Rejected;
        ReviewDecisionNotes = NormalizeReviewNotes(reviewNotes, "rejection");
        IsOnHold = false;
        HoldReason = null;
        HeldAtUtc = null;
        HeldByUserId = null;
        Touch(actorUserId);
    }

    public void Hold(long actorUserId, string holdReason)
    {
        if (StatusId != WorkflowStatusSeedIds.Submitted)
        {
            throw new InvalidOperationException("Only submitted customer profiles can be placed on hold.");
        }

        IsOnHold = true;
        HoldReason = NormalizeReviewNotes(holdReason, "hold");
        HeldAtUtc = DateTime.UtcNow;
        HeldByUserId = actorUserId;
        Touch(actorUserId);
    }

    public void Reopen(long actorUserId, string reopenReason)
    {
        if (StatusId != WorkflowStatusSeedIds.Returned
            && StatusId != WorkflowStatusSeedIds.Rejected
            && !IsOnHold)
        {
            throw new InvalidOperationException("Only returned, rejected, or on-hold customer profiles can be reopened.");
        }

        StatusId = WorkflowStatusSeedIds.Draft;
        IsOnHold = false;
        HoldReason = null;
        HeldAtUtc = null;
        HeldByUserId = null;
        ReopenedAtUtc = DateTime.UtcNow;
        ReopenedByUserId = actorUserId;
        ReopenReason = NormalizeReviewNotes(reopenReason, "reopen");
        Touch(actorUserId);
    }

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

        if (!string.Equals(TaxProfileStatusCode, TaxProfileStatus.Approved, StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException("Customer activation requires approved tax-profile status.");
        }

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

    private static string NormalizeClassificationCode(string classificationCode)
    {
        var normalized = classificationCode?.Trim().ToUpperInvariant();
        return normalized switch
        {
            "GOVERNMENT" => normalized,
            "COMPANY" => normalized,
            "INSTITUTIONAL" => normalized,
            _ => throw new InvalidOperationException("Customer classification must be GOVERNMENT, COMPANY, or INSTITUTIONAL.")
        };
    }

    private static string NormalizeReviewNotes(string reviewNotes, string actionName)
    {
        var normalized = string.IsNullOrWhiteSpace(reviewNotes) ? null : reviewNotes.Trim();
        if (string.IsNullOrWhiteSpace(normalized))
        {
            throw new InvalidOperationException($"Customer profile {actionName} reason is required.");
        }

        return normalized;
    }

    private static class TaxProfileStatus
    {
        public const string Pending = "PENDING";
        public const string Approved = "APPROVED";
    }
}
