using CashLineERP.Contracts.Tax;

namespace CashLineERP.Application.Tax;

public sealed class EtaComplianceService : IEtaComplianceService
{
    private readonly IEtaComplianceRepository _repository;

    public EtaComplianceService(IEtaComplianceRepository repository)
    {
        _repository = repository;
    }

    public EtaPayloadPreviewResponse? GetPayloadPreview(long accountsReceivableInvoiceId)
    {
        var preview = _repository.GetPayloadPreview(accountsReceivableInvoiceId);
        return preview is null
            ? null
            : new EtaPayloadPreviewResponse(
                preview.AccountsReceivableInvoiceId,
                preview.DocumentNumber,
                preview.NormalizedDocumentType,
                preview.PayloadText,
                preview.IsStatutoryReady,
                preview.ValidationMessages);
    }

    public EtaSubmissionValidationPackResponse? GetSubmissionValidationPack(long accountsReceivableInvoiceId)
    {
        var pack = _repository.GetSubmissionValidationPack(accountsReceivableInvoiceId);
        return pack is null
            ? null
            : new EtaSubmissionValidationPackResponse(
                pack.AccountsReceivableInvoiceId,
                pack.DocumentNumber,
                pack.NormalizedDocumentType,
                pack.SellerBranchCode,
                pack.SellerTaxBranchCode,
                pack.BuyerCode,
                pack.IsValid,
                pack.Rules.Select(rule => new EtaSubmissionValidationRuleResponse(
                    rule.RuleCode,
                    rule.RuleGroup,
                    rule.IsSatisfied,
                    rule.Message)).ToArray());
    }

    public IReadOnlyCollection<EtaSubmissionLogResponse> GetSubmissionLogs(long accountsReceivableInvoiceId) =>
        _repository.GetSubmissionLogs(accountsReceivableInvoiceId).Select(Map).ToArray();

    public IReadOnlyCollection<EtaDocumentEventResponse> GetDocumentEvents(long accountsReceivableInvoiceId) =>
        _repository.GetDocumentEvents(accountsReceivableInvoiceId).Select(Map).ToArray();

    public EtaSubmissionLogResponse SubmitInvoice(long accountsReceivableInvoiceId, SubmitInvoiceToEtaRequest request) =>
        Map(_repository.SubmitInvoice(accountsReceivableInvoiceId, request.ActorUserId, request.PayloadText));

    public EtaSubmissionLogResponse ResubmitRejectedInvoice(long accountsReceivableInvoiceId, long etaSubmissionLogId, ResubmitEtaInvoiceRequest request) =>
        Map(_repository.ResubmitRejectedInvoice(
            accountsReceivableInvoiceId,
            etaSubmissionLogId,
            request.ActorUserId,
            request.PayloadText,
            request.CorrectionNotes));

    public EtaSubmissionLogResponse RecordInvoiceResponse(long accountsReceivableInvoiceId, long etaSubmissionLogId, RecordEtaInvoiceResponseRequest request) =>
        Map(_repository.RecordInvoiceResponse(
            accountsReceivableInvoiceId,
            etaSubmissionLogId,
            request.ActorUserId,
            request.StatusCode,
            request.EtaUuid,
            request.ResponseText,
            request.ErrorCode,
            request.ErrorMessage,
            request.NotesText));

    public IReadOnlyCollection<EtaTransmissionAttemptResponse> GetTransmissions(long accountsReceivableInvoiceId) =>
        _repository.GetTransmissions(accountsReceivableInvoiceId).Select(Map).ToArray();

    public EtaTransmissionAttemptResponse? GetTransmission(long etaTransmissionAttemptId)
    {
        var transmission = _repository.GetTransmission(etaTransmissionAttemptId);
        return transmission is null ? null : Map(transmission);
    }

    public IReadOnlyCollection<EtaTransmissionAttemptResponse> GetPendingAuthorityStatusPollBatch(int batchSize) =>
        _repository.GetPendingAuthorityStatusPollBatch(batchSize).Select(Map).ToArray();

    public EtaTransmissionAttemptResponse? FindPendingTransmissionByCorrelation(string correlationKey)
    {
        var transmission = _repository.FindPendingTransmissionByCorrelation(correlationKey);
        return transmission is null ? null : Map(transmission);
    }

    public EtaTransmissionAttemptResponse QueueTransmission(long accountsReceivableInvoiceId, long etaSubmissionLogId, long actorUserId) =>
        Map(_repository.QueueTransmission(accountsReceivableInvoiceId, etaSubmissionLogId, actorUserId));

    public DispatchEtaTransmissionBatchResultResponse DispatchQueuedTransmissions(DispatchEtaTransmissionBatchRequest request)
    {
        var result = _repository.DispatchQueuedTransmissions(new DispatchEtaTransmissionBatchCommand(
            request.ProcessorCode,
            request.BatchSize));

        return new DispatchEtaTransmissionBatchResultResponse(
            result.ProcessorCode,
            result.RequestedBatchSize,
            result.DispatchedCount,
            result.Transmissions.Select(Map).ToArray());
    }

    public PollEtaTransmissionBatchResultResponse PollDispatchedTransmissions(PollEtaTransmissionBatchRequest request)
    {
        var result = _repository.PollDispatchedTransmissions(new PollEtaTransmissionBatchCommand(
            request.ProcessorCode,
            request.BatchSize));

        return new PollEtaTransmissionBatchResultResponse(
            result.ProcessorCode,
            result.RequestedBatchSize,
            result.PolledCount,
            result.Transmissions.Select(Map).ToArray());
    }

    public EtaTransmissionAttemptResponse MarkTransmissionAccepted(long etaTransmissionAttemptId, long actorUserId, string processorCode, string etaUuid, string? responseText) =>
        Map(_repository.MarkTransmissionAccepted(etaTransmissionAttemptId, actorUserId, processorCode, etaUuid, responseText));

    public EtaTransmissionAttemptResponse MarkTransmissionRejected(long etaTransmissionAttemptId, long actorUserId, string processorCode, string errorText, string? responseText, string? errorCode) =>
        Map(_repository.MarkTransmissionRejected(etaTransmissionAttemptId, actorUserId, processorCode, errorText, responseText, errorCode));

    public EtaTransmissionAttemptResponse FailTransmission(long etaTransmissionAttemptId, long actorUserId, FailEtaTransmissionRequest request) =>
        Map(_repository.FailTransmission(
            etaTransmissionAttemptId,
            actorUserId,
            request.ErrorText,
            request.ResponseText));

    public EtaTransmissionAttemptResponse RetryTransmission(long etaTransmissionAttemptId, long actorUserId, RetryEtaTransmissionRequest request) =>
        Map(_repository.RetryTransmission(etaTransmissionAttemptId, new RetryEtaTransmissionCommand(
            actorUserId,
            request.RetryReasonText,
            request.DelaySeconds)));

    private static EtaSubmissionLogResponse Map(EtaSubmissionLogView log) =>
        new(
            log.EtaSubmissionLogId,
            log.AccountsReceivableInvoiceId,
            log.SubmissionReference,
            log.EtaUuid,
            log.StatusCode,
            log.SubmittedAtUtc,
            log.ResponseAtUtc,
            log.ErrorCode,
            log.ErrorMessage,
            log.PayloadText,
            log.ResponseText);

    private static EtaDocumentEventResponse Map(EtaDocumentEventView evt) =>
        new(
            evt.EtaDocumentEventId,
            evt.AccountsReceivableInvoiceId,
            evt.EtaSubmissionLogId,
            evt.EventType,
            evt.EventStatus,
            evt.EventTimestampUtc,
            evt.ActorUserId,
            evt.PayloadText,
            evt.ResponseText,
            evt.NotesText);

    private static EtaTransmissionAttemptResponse Map(EtaTransmissionAttemptView transmission) =>
        new(
            transmission.EtaTransmissionAttemptId,
            transmission.AccountsReceivableInvoiceId,
            transmission.EtaSubmissionLogId,
            transmission.IntegrationEndpointId,
            transmission.EndpointCode,
            transmission.TransmissionNumber,
            transmission.StatusCode,
            transmission.PayloadText,
            transmission.SignedPayloadText,
            transmission.SignatureText,
            transmission.AdapterReference,
            transmission.ResponseText,
            transmission.AttemptNumber,
            transmission.AvailableAtUtc,
            transmission.DispatchedAtUtc,
            transmission.DispatchedByProcess,
            transmission.PolledAtUtc,
            transmission.PolledByProcess,
            transmission.LastErrorText,
            transmission.CreatedAtUtc);
}
