package prmap

import (
	"strings"

	"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/agg"
	"github.com/tech/sendico/payments/storage/model"
	gatewayv1 "github.com/tech/sendico/pkg/proto/common/gateway/v1"
	orchestrationv2 "github.com/tech/sendico/pkg/proto/payments/orchestration/v2"
	sharedv1 "github.com/tech/sendico/pkg/proto/payments/shared/v1"
)

func normalizeAggregateState(state agg.State) (agg.State, bool) {
	switch strings.ToLower(strings.TrimSpace(string(state))) {
	case string(agg.StateCreated):
		return agg.StateCreated, true
	case string(agg.StateExecuting):
		return agg.StateExecuting, true
	case string(agg.StateNeedsAttention):
		return agg.StateNeedsAttention, true
	case string(agg.StateSettled):
		return agg.StateSettled, true
	case string(agg.StateFailed):
		return agg.StateFailed, true
	default:
		return agg.StateUnspecified, false
	}
}

func normalizeStepState(state agg.StepState) (agg.StepState, bool) {
	switch strings.ToLower(strings.TrimSpace(string(state))) {
	case string(agg.StepStatePending):
		return agg.StepStatePending, true
	case string(agg.StepStateRunning):
		return agg.StepStateRunning, true
	case string(agg.StepStateCompleted):
		return agg.StepStateCompleted, true
	case string(agg.StepStateFailed):
		return agg.StepStateFailed, true
	case string(agg.StepStateNeedsAttention):
		return agg.StepStateNeedsAttention, true
	case string(agg.StepStateSkipped):
		return agg.StepStateSkipped, true
	default:
		return agg.StepStateUnspecified, false
	}
}

func mapAggregateState(state agg.State) orchestrationv2.OrchestrationState {
	switch normalized, _ := normalizeAggregateState(state); normalized {
	case agg.StateCreated:
		return orchestrationv2.OrchestrationState_ORCHESTRATION_STATE_CREATED
	case agg.StateExecuting:
		return orchestrationv2.OrchestrationState_ORCHESTRATION_STATE_EXECUTING
	case agg.StateNeedsAttention:
		return orchestrationv2.OrchestrationState_ORCHESTRATION_STATE_NEEDS_ATTENTION
	case agg.StateSettled:
		return orchestrationv2.OrchestrationState_ORCHESTRATION_STATE_SETTLED
	case agg.StateFailed:
		return orchestrationv2.OrchestrationState_ORCHESTRATION_STATE_FAILED
	default:
		return orchestrationv2.OrchestrationState_ORCHESTRATION_STATE_UNSPECIFIED
	}
}

func mapStepState(state agg.StepState) orchestrationv2.StepExecutionState {
	switch normalized, _ := normalizeStepState(state); normalized {
	case agg.StepStatePending:
		return orchestrationv2.StepExecutionState_STEP_EXECUTION_STATE_PENDING
	case agg.StepStateRunning:
		return orchestrationv2.StepExecutionState_STEP_EXECUTION_STATE_RUNNING
	case agg.StepStateCompleted:
		return orchestrationv2.StepExecutionState_STEP_EXECUTION_STATE_COMPLETED
	case agg.StepStateFailed:
		return orchestrationv2.StepExecutionState_STEP_EXECUTION_STATE_FAILED
	case agg.StepStateNeedsAttention:
		return orchestrationv2.StepExecutionState_STEP_EXECUTION_STATE_NEEDS_ATTENTION
	case agg.StepStateSkipped:
		return orchestrationv2.StepExecutionState_STEP_EXECUTION_STATE_SKIPPED
	default:
		return orchestrationv2.StepExecutionState_STEP_EXECUTION_STATE_UNSPECIFIED
	}
}

func mapReportVisibility(visibility model.ReportVisibility) orchestrationv2.ReportVisibility {
	switch model.NormalizeReportVisibility(visibility) {
	case model.ReportVisibilityHidden:
		return orchestrationv2.ReportVisibility_REPORT_VISIBILITY_HIDDEN
	case model.ReportVisibilityUser:
		return orchestrationv2.ReportVisibility_REPORT_VISIBILITY_USER
	case model.ReportVisibilityBackoffice:
		return orchestrationv2.ReportVisibility_REPORT_VISIBILITY_BACKOFFICE
	case model.ReportVisibilityAudit:
		return orchestrationv2.ReportVisibility_REPORT_VISIBILITY_AUDIT
	default:
		return orchestrationv2.ReportVisibility_REPORT_VISIBILITY_UNSPECIFIED
	}
}

func inferFailureCategory(failureCode string) sharedv1.PaymentFailureCode {
	code := strings.ToLower(strings.TrimSpace(failureCode))
	switch {
	case strings.Contains(code, "balance"), strings.Contains(code, "insufficient_funds"):
		return sharedv1.PaymentFailureCode_FAILURE_BALANCE
	case strings.Contains(code, "ledger"):
		return sharedv1.PaymentFailureCode_FAILURE_LEDGER
	case strings.Contains(code, "fx"):
		return sharedv1.PaymentFailureCode_FAILURE_FX
	case strings.Contains(code, "chain"), strings.Contains(code, "crypto"), strings.Contains(code, "provider"), strings.Contains(code, "card"):
		return sharedv1.PaymentFailureCode_FAILURE_CHAIN
	case strings.Contains(code, "fee"), strings.Contains(code, "charge"):
		return sharedv1.PaymentFailureCode_FAILURE_FEES
	case strings.Contains(code, "policy"), strings.Contains(code, "risk"), strings.Contains(code, "compliance"):
		return sharedv1.PaymentFailureCode_FAILURE_POLICY
	default:
		return sharedv1.PaymentFailureCode_FAILURE_UNSPECIFIED
	}
}

func inferRail(kind string, stepCode string) gatewayv1.Rail {
	all := strings.ToLower(strings.TrimSpace(kind + " " + stepCode))
	switch {
	case strings.Contains(all, "ledger"):
		return gatewayv1.Rail_RAIL_LEDGER
	case strings.Contains(all, "card_payout"), strings.Contains(all, "card"):
		return gatewayv1.Rail_RAIL_CARD
	case strings.Contains(all, "provider_settlement"),
		strings.Contains(all, "settlement"),
		strings.Contains(all, "provider"):
		return gatewayv1.Rail_RAIL_SETTLEMENT
	case strings.Contains(all, "fiat_onramp"), strings.Contains(all, "onramp"):
		return gatewayv1.Rail_RAIL_ONRAMP
	case strings.Contains(all, "crypto"), strings.Contains(all, "chain"), strings.Contains(all, "tx"):
		return gatewayv1.Rail_RAIL_CRYPTO
	default:
		return gatewayv1.Rail_RAIL_UNSPECIFIED
	}
}
