package p2p

import (
	"context"
	"crypto/rand"
	"fmt"

	host "github.com/libp2p/go-libp2p/core/host"
	libp2p "github.com/libp2p/go-libp2p"
	"github.com/libp2p/go-libp2p/core/crypto"
	"github.com/libp2p/go-libp2p/core/network"
	"github.com/libp2p/go-libp2p/core/peer"
)

const ProtocolID = "/dejo/1.0.0"

// P2PNode representa um nó da rede P2P da DEJO.
type P2PNode struct {
	Host     host.Host
	PeerChan chan peer.AddrInfo
}

// NewP2PNode cria um novo nó libp2p.
func NewP2PNode(ctx context.Context) (*P2PNode, error) {
	priv, _, _ := crypto.GenerateKeyPairWithReader(crypto.Ed25519, -1, rand.Reader)

	h, err := libp2p.New(
		libp2p.Identity(priv),
		libp2p.ListenAddrStrings("/ip4/0.0.0.0/tcp/0"),
	)
	if err != nil {
		return nil, err
	}

	node := &P2PNode{
		Host:     h,
		PeerChan: make(chan peer.AddrInfo),
	}

	// Handler para conexões recebidas
	h.SetStreamHandler(ProtocolID, func(s network.Stream) {
		buf := make([]byte, 1024)
		s.Read(buf)
		fmt.Printf("📡 Recebido: %s\n", string(buf))
		s.Close()
	})

	return node, nil
}

// HandlePeerFound é chamado quando um novo peer é descoberto.
func (p *P2PNode) HandlePeerFound(pi peer.AddrInfo) {
	fmt.Println("👋 Novo peer descoberto:", pi.ID)
	p.PeerChan <- pi
}

// ConnectToPeers tenta se conectar aos peers encontrados.
func (p *P2PNode) ConnectToPeers(ctx context.Context) {
	go func() {
		for pi := range p.PeerChan {
			if err := p.Host.Connect(ctx, pi); err != nil {
				fmt.Println("Erro ao conectar ao peer:", err)
			} else {
				fmt.Println("🔗 Conectado ao peer:", pi.ID)
			}
		}
	}()
}

// Broadcast envia uma mensagem para todos os peers conectados.
func (p *P2PNode) Broadcast(msg string) {
	for _, c := range p.Host.Network().Peers() {
		stream, err := p.Host.NewStream(context.Background(), c, ProtocolID)
		if err != nil {
			fmt.Println("Erro ao abrir stream:", err)
			continue
		}
		stream.Write([]byte(msg))
		stream.Close()
	}
}