package ledger

import (
	"sync"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"
)

type journalEntryType string

const (
	journalEntryTypeCredit   journalEntryType = "credit"
	journalEntryTypeDebit    journalEntryType = "debit"
	journalEntryTypeTransfer journalEntryType = "transfer"
	journalEntryTypeFX       journalEntryType = "fx"
)

type journalEntryStatus string

const (
	journalEntryStatusAttempted journalEntryStatus = "attempted"
	journalEntryStatusSuccess   journalEntryStatus = "success"
	journalEntryStatusError     journalEntryStatus = "error"
)

type journalEntryErrorType string

const (
	journalEntryErrorNotImplemented        journalEntryErrorType = "not_implemented"
	journalEntryErrorFailed                journalEntryErrorType = "failed"
	journalEntryErrorIdempotencyCheck      journalEntryErrorType = "idempotency_check_failed"
	journalEntryErrorAccountResolve        journalEntryErrorType = "account_resolve_failed"
	journalEntryErrorAccountInvalid        journalEntryErrorType = "account_invalid"
	journalEntryErrorContraResolve         journalEntryErrorType = "contra_resolve_failed"
	journalEntryErrorContraMissingID       journalEntryErrorType = "contra_missing_id"
	journalEntryErrorSystemAccountResolve  journalEntryErrorType = "system_account_resolve_failed"
	journalEntryErrorSystemAccountInvalid  journalEntryErrorType = "system_account_invalid"
	journalEntryErrorSystemAccountMissing  journalEntryErrorType = "system_account_missing_id"
	journalEntryErrorUnbalancedAfterContra journalEntryErrorType = "unbalanced_after_contra"
	journalEntryErrorTransactionFailed     journalEntryErrorType = "transaction_failed"
)

var (
	metricsOnce sync.Once

	// Journal entry operations
	journalEntriesTotal *prometheus.CounterVec
	journalEntryLatency *prometheus.HistogramVec
	journalEntryErrors  *prometheus.CounterVec

	// Balance operations
	balanceQueriesTotal *prometheus.CounterVec
	balanceQueryLatency *prometheus.HistogramVec

	// Transaction amounts
	transactionAmounts *prometheus.HistogramVec

	// Account operations
	accountOperationsTotal *prometheus.CounterVec

	// Idempotency
	duplicateRequestsTotal *prometheus.CounterVec
)

func initMetrics() {
	metricsOnce.Do(func() {
		// Journal entries posted by type
		journalEntriesTotal = promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "ledger_journal_entries_total",
				Help: "Total number of journal entries posted to the ledger",
			},
			[]string{"entry_type", "status"}, // entry_type: credit, debit, transfer, fx, fee, adjust, reverse
		)

		// Journal entry processing latency
		journalEntryLatency = promauto.NewHistogramVec(
			prometheus.HistogramOpts{
				Name:    "ledger_journal_entry_duration_seconds",
				Help:    "Duration of journal entry posting operations",
				Buckets: []float64{.001, .005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10},
			},
			[]string{"entry_type"},
		)

		// Journal entry errors by type
		journalEntryErrors = promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "ledger_journal_entry_errors_total",
				Help: "Total number of journal entry posting errors",
			},
			[]string{"entry_type", "error_type"}, // error_type: validation, insufficient_funds, db_error, etc.
		)

		// Balance queries
		balanceQueriesTotal = promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "ledger_balance_queries_total",
				Help: "Total number of balance queries",
			},
			[]string{"status"}, // success, error
		)

		// Balance query latency
		balanceQueryLatency = promauto.NewHistogramVec(
			prometheus.HistogramOpts{
				Name:    "ledger_balance_query_duration_seconds",
				Help:    "Duration of balance query operations",
				Buckets: prometheus.DefBuckets,
			},
			[]string{"status"},
		)

		// Transaction amounts (in normalized form)
		transactionAmounts = promauto.NewHistogramVec(
			prometheus.HistogramOpts{
				Name:    "ledger_transaction_amount",
				Help:    "Distribution of transaction amounts",
				Buckets: []float64{1, 10, 50, 100, 500, 1000, 5000, 10000, 50000, 100000},
			},
			[]string{"currency", "entry_type"},
		)

		// Account operations
		accountOperationsTotal = promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "ledger_account_operations_total",
				Help: "Total number of account-level operations",
			},
			[]string{"operation", "status"}, // operation: create, freeze, unfreeze
		)

		// Duplicate/idempotent requests
		duplicateRequestsTotal = promauto.NewCounterVec(
			prometheus.CounterOpts{
				Name: "ledger_duplicate_requests_total",
				Help: "Total number of duplicate requests detected via idempotency keys",
			},
			[]string{"entry_type"},
		)
	})
}

// Metric recording helpers

func recordJournalEntry(entryType journalEntryType, status journalEntryStatus, durationSeconds float64) {
	initMetrics()
	journalEntriesTotal.WithLabelValues(string(entryType), string(status)).Inc()
	journalEntryLatency.WithLabelValues(string(entryType)).Observe(durationSeconds)
}

func recordJournalEntryError(entryType journalEntryType, errorType journalEntryErrorType) {
	initMetrics()
	journalEntryErrors.WithLabelValues(string(entryType), string(errorType)).Inc()
	journalEntriesTotal.WithLabelValues(string(entryType), string(journalEntryStatusError)).Inc()
}

func recordBalanceQuery(status string, durationSeconds float64) {
	initMetrics()
	balanceQueriesTotal.WithLabelValues(status).Inc()
	balanceQueryLatency.WithLabelValues(status).Observe(durationSeconds)
}

func recordTransactionAmount(currency string, entryType journalEntryType, amount float64) {
	initMetrics()
	transactionAmounts.WithLabelValues(currency, string(entryType)).Observe(amount)
}

func recordAccountOperation(operation, status string) {
	initMetrics()
	accountOperationsTotal.WithLabelValues(operation, status).Inc()
}

func recordDuplicateRequest(entryType journalEntryType) {
	initMetrics()
	duplicateRequestsTotal.WithLabelValues(string(entryType)).Inc()
}
