package model

import (
	"strings"
	"time"

	"github.com/tech/sendico/pkg/db/storable"
	pkgmodel "github.com/tech/sendico/pkg/model"
	"github.com/tech/sendico/pkg/mservice"
	moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
)

type ManagedWalletStatus string

const (
	ManagedWalletStatusActive    ManagedWalletStatus = "active"
	ManagedWalletStatusSuspended ManagedWalletStatus = "suspended"
	ManagedWalletStatusClosed    ManagedWalletStatus = "closed"
)

// ManagedWallet represents a user-controlled on-chain wallet managed by the service.
type ManagedWallet struct {
	storable.Base        `bson:",inline" json:",inline"`
	pkgmodel.Describable `bson:",inline" json:",inline"`

	IdempotencyKey  string                `bson:"idempotencyKey" json:"idempotencyKey"`
	WalletRef       string                `bson:"walletRef" json:"walletRef"`
	OrganizationRef string                `bson:"organizationRef" json:"organizationRef"`
	OwnerRef        string                `bson:"ownerRef" json:"ownerRef"`
	Network         pkgmodel.ChainNetwork `bson:"network" json:"network"`
	TokenSymbol     string                `bson:"tokenSymbol" json:"tokenSymbol"`
	ContractAddress string                `bson:"contractAddress" json:"contractAddress"`
	DepositAddress  string                `bson:"depositAddress" json:"depositAddress"`
	KeyReference    string                `bson:"keyReference,omitempty" json:"keyReference,omitempty"`
	Status          ManagedWalletStatus   `bson:"status" json:"status"`
	Metadata        map[string]string     `bson:"metadata,omitempty" json:"metadata,omitempty"`
}

// Collection implements storable.Storable.
func (*ManagedWallet) Collection() string {
	return mservice.ChainWallets
}

// WalletBalance captures computed wallet balances.
type WalletBalance struct {
	storable.Base `bson:",inline" json:",inline"`

	WalletRef       string         `bson:"walletRef" json:"walletRef"`
	Available       *moneyv1.Money `bson:"available" json:"available"`
	NativeAvailable *moneyv1.Money `bson:"nativeAvailable,omitempty" json:"nativeAvailable,omitempty"`
	PendingInbound  *moneyv1.Money `bson:"pendingInbound,omitempty" json:"pendingInbound,omitempty"`
	PendingOutbound *moneyv1.Money `bson:"pendingOutbound,omitempty" json:"pendingOutbound,omitempty"`
	CalculatedAt    time.Time      `bson:"calculatedAt" json:"calculatedAt"`
}

// Collection implements storable.Storable.
func (*WalletBalance) Collection() string {
	return mservice.ChainWalletBalances
}

// ManagedWalletFilter describes list filters.
type ManagedWalletFilter struct {
	OrganizationRef string
	// OwnerRefFilter is a 3-state filter:
	// - nil: no filter on owner_ref (return all)
	// - pointer to empty string: filter for wallets where owner_ref is empty
	// - pointer to a value: filter for wallets where owner_ref matches
	OwnerRefFilter *string
	Network        pkgmodel.ChainNetwork
	TokenSymbol    string
	Cursor         string
	Limit          int32
}

// ManagedWalletList contains paginated wallet results.
type ManagedWalletList struct {
	Items      []ManagedWallet
	NextCursor string
}

// Normalize trims string fields for consistent indexing.
func (m *ManagedWallet) Normalize() {
	m.IdempotencyKey = strings.TrimSpace(m.IdempotencyKey)
	m.WalletRef = strings.TrimSpace(m.WalletRef)
	m.OrganizationRef = strings.TrimSpace(m.OrganizationRef)
	m.OwnerRef = strings.TrimSpace(m.OwnerRef)
	m.Name = strings.TrimSpace(m.Name)
	if m.Description != nil {
		desc := strings.TrimSpace(*m.Description)
		if desc == "" {
			m.Description = nil
		} else {
			m.Description = &desc
		}
	}
	m.TokenSymbol = strings.TrimSpace(strings.ToUpper(m.TokenSymbol))
	m.ContractAddress = strings.TrimSpace(strings.ToLower(m.ContractAddress))
	m.DepositAddress = normalizeWalletAddress(m.DepositAddress)
	m.KeyReference = strings.TrimSpace(m.KeyReference)
}

// Normalize trims wallet balance identifiers.
func (b *WalletBalance) Normalize() {
	b.WalletRef = strings.TrimSpace(b.WalletRef)
}

func normalizeWalletAddress(address string) string {
	trimmed := strings.TrimSpace(address)
	if trimmed == "" {
		return ""
	}
	if isHexAddress(trimmed) {
		return strings.ToLower(trimmed)
	}
	return trimmed
}

func isHexAddress(value string) bool {
	trimmed := strings.TrimPrefix(strings.TrimSpace(value), "0x")
	if len(trimmed) != 40 && len(trimmed) != 42 {
		return false
	}
	for _, r := range trimmed {
		switch {
		case r >= '0' && r <= '9':
		case r >= 'a' && r <= 'f':
		case r >= 'A' && r <= 'F':
		default:
			return false
		}
	}
	return true
}
