package state

import (
	"encoding/gob"
	"fmt"
	"os"
	"sync"
	"dejo_node/internal/staking"
	"dejo_node/internal/transactions"
)

type State struct {
	mu       sync.RWMutex
	Balances map[string]uint64
}

func NewState() *State {
	return &State{
		Balances: make(map[string]uint64),
	}
}

func (s *State) Apply(tx *transactions.Transaction) error {
	s.mu.Lock()
	defer s.mu.Unlock()

	if tx.From == tx.To {
		return fmt.Errorf("transação inválida: origem = destino")
	}
	if tx.Value <= 0 {
		return fmt.Errorf("valor zero não permitido")
	}
	fromBal := s.Balances[tx.From]
	if fromBal < uint64(tx.Value) {
		return fmt.Errorf("saldo insuficiente: %s tem %d, precisa de %f", tx.From, fromBal, tx.Value)
	}
	s.Balances[tx.From] -= uint64(tx.Value)
	s.Balances[tx.To] += uint64(tx.Value)
	return nil
}

func (s *State) Transfer(from, to string, amount uint64) error {
	s.mu.Lock()
	defer s.mu.Unlock()

	if s.Balances[from] < amount {
		return fmt.Errorf("saldo insuficiente")
	}
	s.Balances[from] -= amount
	s.Balances[to] += amount
	return nil
}

func (s *State) Mint(to string, amount uint64) {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.Balances[to] += amount
}

func (s *State) Stake(address string, amount uint64, dur int64) error {
	if err := s.Transfer(address, "staking_pool", amount); err != nil {
		return err
	}
	return staking.NewStakingStore().Stake(address, amount, uint64(dur))
}

func (s *State) Unstake(address string) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	if err := staking.NewStakingStore().Unstake(address); err != nil {
		return err
	}
	s.Balances[address] += 1000 // Exemplo: devolve saldo fixo
	return nil
}

func (s *State) SaveToDisk(path string) error {
	f, err := os.Create(path)
	if err != nil {
		return err
	}
	defer f.Close()
	enc := gob.NewEncoder(f)
	return enc.Encode(s.Balances)
}

func (s *State) LoadFromDisk(path string) error {
	f, err := os.Open(path)
	if err != nil {
		if os.IsNotExist(err) {
			s.Balances = make(map[string]uint64)
			return nil
		}
		return err
	}
	defer f.Close()
	dec := gob.NewDecoder(f)
	return dec.Decode(&s.Balances)
}