package arkiv

import (
	"math/big"
	"strconv"
	"time"

	"github.com/ethereum/go-ethereum/common"
)

// Schema names (expiry days: 7/182/7 per issue #32; LPEpoch 182).
const (
	SchemaFunding  = "perpvault.FundingRate"
	SchemaDecision = "perpvault.VaultDecision"
	SchemaSnapshot = "perpvault.VaultSnapshot"
	SchemaLPEpoch  = "perpvault.LPEpoch"
)

var e12 = big.NewInt(1e12)

// from6 turns a 6dp int into dec raw (x1e12).
func from6(v *big.Int) *big.Int { return new(big.Int).Mul(v, e12) }

func ts(t time.Time) Attr { return U64("ts", uint64(t.Unix())) }

// FundingRate is one funding print. Rate 1e18-scaled, MarkPx 1e6.
type FundingRate struct {
	Market     string
	TS         time.Time
	Rate       *big.Int
	MarkPx     *big.Int
	FundingIdx *big.Int // cumulative index; event carries no index px
}

func (r FundingRate) Entity() Entity {
	return Entity{SchemaFunding, 7, []Attr{
		Str("market", r.Market), ts(r.TS),
		Dec("rate", r.Rate), Dec("markPx", from6(r.MarkPx)), Dec("fundingIdx", r.FundingIdx),
	}}
}

// VaultDecision is a mode change. FundingRefs = "block-log,..." of justifying prints.
type VaultDecision struct {
	TS          time.Time
	Market      string
	Mode        string // HEDGED | FLAT
	Reason      string // <=128 bytes
	FundingRefs string
}

func (d VaultDecision) Entity() Entity {
	return Entity{SchemaDecision, 182, []Attr{
		ts(d.TS), Str("market", d.Market), Str("mode", d.Mode),
		Str("reason", d.Reason), Bytes("fundingRefs", []byte(d.FundingRefs)),
	}}
}

// VaultSnapshot is the hourly state. NAV USDC 6dp, SharePrice 1e18.
type VaultSnapshot struct {
	TS         time.Time
	NAV        *big.Int
	SharePrice *big.Int
	Markets    []byte // JSON: per-market mode + sizes
}

func (s VaultSnapshot) Entity() Entity {
	return Entity{SchemaSnapshot, 7, []Attr{
		ts(s.TS), Dec("nav", from6(s.NAV)), Dec("sharePrice", s.SharePrice), Bytes("markets", s.Markets),
	}}
}

// LPEpoch is one LP payout. Shares per pool (BTC, ETH, HYPE) and FromFunding USDC 6dp; pot = sum(shares).
type LPEpoch struct {
	Epoch       uint64
	TS          time.Time
	Shares      [3]*big.Int
	FromFunding *big.Int
	Ref, Tx     common.Hash
}

func (e LPEpoch) Entity() Entity {
	pot := new(big.Int)
	for _, s := range e.Shares {
		pot.Add(pot, s)
	}
	return Entity{SchemaLPEpoch, 182, []Attr{
		U64("epoch", e.Epoch), ts(e.TS),
		Dec("shareBTC", from6(e.Shares[0])), Dec("shareETH", from6(e.Shares[1])), Dec("shareHYPE", from6(e.Shares[2])),
		Dec("pot", from6(pot)), Dec("fromFunding", from6(e.FromFunding)),
		Bytes("ref", e.Ref[:]), Bytes("tx", e.Tx[:]),
	}}
}

// Ref formats one funding print for VaultDecision.FundingRefs.
func Ref(block uint64, log uint) string {
	return strconv.FormatUint(block, 10) + "-" + strconv.FormatUint(uint64(log), 10)
}
