package dao

import (
	"encoding/json"
	"log"
	"sync"
	"time"
)

type ProposalType string

const (
	GenericProposal    ProposalType = "GENERIC"
	ParamChangeProposal ProposalType = "PARAM_CHANGE"
)

type Proposal struct {
	ID        uint64
	Title     string
	Content   string
	Type      ProposalType
	Creator   string
	CreatedAt int64
	Duration  int64 // segundos até expiração
	Votes     map[string]bool
	Closed    bool
	Approved  bool
	mu        sync.Mutex
}

func NewProposal(id uint64, title, content, creator string, typ ProposalType, duration int64) *Proposal {
	return &Proposal{
		ID:        id,
		Title:     title,
		Content:   content,
		Type:      typ,
		Creator:   creator,
		CreatedAt: time.Now().Unix(),
		Duration:  duration,
		Votes:     make(map[string]bool),
		Closed:    false,
		Approved:  false,
	}
}

func (p *Proposal) Vote(address string, approve bool) error {
	p.mu.Lock()
	defer p.mu.Unlock()
	if p.Closed {
		return nil
	}
	p.Votes[address] = approve
	return nil
}

func (p *Proposal) Tally(stakes map[string]uint64) (yes uint64, no uint64, total uint64) {
	p.mu.Lock()
	defer p.mu.Unlock()
	for addr, vote := range p.Votes {
		stake := stakes[addr]
		total += stake
		if vote {
			yes += stake
		} else {
			no += stake
		}
	}
	return
}

func (p *Proposal) CheckAndClose(stakes map[string]uint64) {
	if p.Closed {
		return
	}
	if time.Now().Unix() >= p.CreatedAt+p.Duration {
		yes, _, total := p.Tally(stakes)
		if yes*100/total > 66 {
			p.Approved = true
		}
		p.Closed = true
		if p.Approved {
			p.Execute()
		}
	}
}

// Execute aplica os efeitos da proposta, se for do tipo que altera estado
func (p *Proposal) Execute() {
	if p.Type == ParamChangeProposal {
		var payload struct {
			Target string `json:"target"`
			Value  uint64 `json:"value"`
		}
		err := json.Unmarshal([]byte(p.Content), &payload)
		if err != nil {
			log.Printf("❌ Erro ao executar proposta PARAM_CHANGE: %v", err)
			return
		}

		// Exemplo: muda parâmetro global (placeholder)
		log.Printf("⚙️ Aplicando PARAM_CHANGE: %s = %d", payload.Target, payload.Value)
		// Aqui você aplicaria no config real
	}
}