package gateway

import (
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"strings"
	"sync/atomic"

	"github.com/tech/sendico/gateway/aurora/internal/service/provider"
	"github.com/tech/sendico/pkg/merrors"
)

type simulatedCardScenario struct {
	Name           string
	CardNumbers    []string
	CardLast4      []string
	Accepted       bool
	ProviderStatus string
	ErrorCode      string
	ErrorMessage   string
	DispatchError  string
}

type payoutSimulator struct {
	scenarios       []simulatedCardScenario
	defaultScenario simulatedCardScenario
	seq             atomic.Uint64
}

const (
	scenarioPANLength   = 16
	scenarioPANVariants = 8
)

var scenarioPANPrefixes = map[string]string{
	"approved_instant":               "22000011",
	"pending_issuer_review":          "22000022",
	"insufficient_funds":             "22000033",
	"issuer_unavailable_retryable":   "22000044",
	"stolen_card":                    "22000055",
	"do_not_honor":                   "22000066",
	"expired_card":                   "22000077",
	"provider_timeout_transport":     "22000088",
	"provider_unreachable_transport": "22000098",
	"provider_maintenance":           "22000097",
	"provider_system_malfunction":    "22000096",
}

func newPayoutSimulator() *payoutSimulator {
	return &payoutSimulator{
		scenarios: []simulatedCardScenario{
			{
				Name:           "approved_instant",
				CardNumbers:    mustScenarioPANs("approved_instant"),
				Accepted:       true,
				ProviderStatus: "success",
				ErrorCode:      "00",
				ErrorMessage:   "Approved by issuer",
			},
			{
				Name:           "pending_issuer_review",
				CardNumbers:    mustScenarioPANs("pending_issuer_review"),
				Accepted:       true,
				ProviderStatus: "processing",
				ErrorCode:      "P01",
				ErrorMessage:   "Pending issuer review",
			},
			{
				Name:         "insufficient_funds",
				CardNumbers:  mustScenarioPANs("insufficient_funds"),
				CardLast4:    []string{"3333"},
				Accepted:     false,
				ErrorCode:    "51",
				ErrorMessage: "Insufficient funds",
			},
			{
				Name:         "issuer_unavailable_retryable",
				CardNumbers:  mustScenarioPANs("issuer_unavailable_retryable"),
				CardLast4:    []string{"4444"},
				Accepted:     false,
				ErrorCode:    "10101",
				ErrorMessage: "Issuer temporary unavailable, retry later",
			},
			{
				Name:         "stolen_card",
				CardNumbers:  mustScenarioPANs("stolen_card"),
				CardLast4:    []string{"5555"},
				Accepted:     false,
				ErrorCode:    "43",
				ErrorMessage: "Stolen card, pickup",
			},
			{
				Name:         "do_not_honor",
				CardNumbers:  mustScenarioPANs("do_not_honor"),
				CardLast4:    []string{"6666"},
				Accepted:     false,
				ErrorCode:    "05",
				ErrorMessage: "Do not honor",
			},
			{
				Name:         "expired_card",
				CardNumbers:  mustScenarioPANs("expired_card"),
				CardLast4:    []string{"7777"},
				Accepted:     false,
				ErrorCode:    "54",
				ErrorMessage: "Expired card",
			},
			{
				Name:          "provider_timeout_transport",
				CardNumbers:   mustScenarioPANs("provider_timeout_transport"),
				CardLast4:     []string{"8888"},
				DispatchError: "provider timeout while calling payout endpoint",
			},
			{
				Name:          "provider_unreachable_transport",
				CardNumbers:   mustScenarioPANs("provider_unreachable_transport"),
				CardLast4:     []string{"9998"},
				DispatchError: "provider host unreachable",
			},
			{
				Name:         "provider_maintenance",
				CardNumbers:  mustScenarioPANs("provider_maintenance"),
				CardLast4:    []string{"9997"},
				Accepted:     false,
				ErrorCode:    "91",
				ErrorMessage: "Issuer or switch is inoperative",
			},
			{
				Name:         "provider_system_malfunction",
				CardNumbers:  mustScenarioPANs("provider_system_malfunction"),
				CardLast4:    []string{"9996"},
				Accepted:     false,
				ErrorCode:    "96",
				ErrorMessage: "System malfunction",
			},
		},
		defaultScenario: simulatedCardScenario{
			Name:           "default_processing",
			Accepted:       true,
			ProviderStatus: "processing",
			ErrorCode:      "P00",
			ErrorMessage:   "Queued for provider processing",
		},
	}
}

func mustScenarioPANs(name string) []string {
	pans := scenarioPANs(name)
	if len(pans) == 0 {
		panic("aurora simulator scenario pan generation failed for " + name)
	}
	return pans
}

func scenarioPANs(name string) []string {
	prefix := strings.TrimSpace(scenarioPANPrefixes[name])
	if prefix == "" {
		return nil
	}
	pans, err := generatePANSeriesWithLuhn(prefix, scenarioPANVariants, scenarioPANLength)
	if err != nil {
		return nil
	}
	return pans
}

func scenarioPAN(name string, index int) string {
	pans := scenarioPANs(name)
	if len(pans) == 0 {
		return ""
	}
	if index < 0 {
		index = 0
	}
	return pans[index%len(pans)]
}

func defaultScenarioPAN() string {
	pan, err := generatePANWithLuhn("22000999", 0, scenarioPANLength)
	if err != nil {
		return ""
	}
	return pan
}

func (s *payoutSimulator) resolveByPAN(pan string) simulatedCardScenario {
	return s.resolve(normalizeCardNumber(pan), "")
}

func (s *payoutSimulator) resolveByMaskedPAN(masked string) simulatedCardScenario {
	digits := normalizeCardNumber(masked)
	last4 := ""
	if len(digits) >= 4 {
		last4 = digits[len(digits)-4:]
	}
	return s.resolve("", last4)
}

func (s *payoutSimulator) resolve(pan, last4 string) simulatedCardScenario {
	if s == nil {
		return simulatedCardScenario{}
	}
	for _, scenario := range s.scenarios {
		for _, value := range scenario.CardNumbers {
			if pan != "" && normalizeCardNumber(value) == pan {
				return scenario
			}
		}
	}
	if strings.TrimSpace(last4) != "" {
		for _, scenario := range s.scenarios {
			if scenarioMatchesLast4(scenario, last4) {
				return scenario
			}
		}
	}
	return s.defaultScenario
}

func (s *payoutSimulator) buildPayoutResult(operationRef string, scenario simulatedCardScenario) (*provider.CardPayoutSendResult, error) {
	if s == nil {
		return &provider.CardPayoutSendResult{
			Accepted:     true,
			StatusCode:   200,
			ErrorCode:    "P00",
			ErrorMessage: "Queued for provider processing",
		}, nil
	}
	if msg := strings.TrimSpace(scenario.DispatchError); msg != "" {
		return nil, merrors.Internal("aurora simulated transport error: " + msg)
	}
	id := s.seq.Add(1)
	ref := strings.TrimSpace(operationRef)
	if ref == "" {
		ref = "card-op"
	}
	statusCode := 200
	if !scenario.Accepted {
		statusCode = 422
	}
	return &provider.CardPayoutSendResult{
		Accepted:          scenario.Accepted,
		ProviderRequestID: fmt.Sprintf("aurora-%s-%06d", ref, id),
		ProviderStatus:    strings.TrimSpace(scenario.ProviderStatus),
		StatusCode:        statusCode,
		ErrorCode:         strings.TrimSpace(scenario.ErrorCode),
		ErrorMessage:      strings.TrimSpace(scenario.ErrorMessage),
	}, nil
}

func scenarioMatchesLast4(scenario simulatedCardScenario, last4 string) bool {
	candidate := strings.TrimSpace(last4)
	if candidate == "" {
		return false
	}
	for _, value := range scenario.CardLast4 {
		if normalizeCardNumber(value) == candidate {
			return true
		}
	}
	for _, value := range scenario.CardNumbers {
		normalized := normalizeCardNumber(value)
		if len(normalized) >= 4 && normalized[len(normalized)-4:] == candidate {
			return true
		}
	}
	return false
}

func normalizeExpiryYear(year uint32) string {
	if year == 0 {
		return ""
	}
	v := int(year)
	if v < 100 {
		v += 2000
	}
	return fmt.Sprintf("%04d", v)
}

func buildSimulatedCardToken(requestID, pan string) string {
	input := strings.TrimSpace(requestID) + "|" + normalizeCardNumber(pan)
	sum := sha256.Sum256([]byte(input))
	return "aur_tok_" + hex.EncodeToString(sum[:8])
}
