package peer

import (
	"encoding/json"
	"io"
	"log"
	"net/http"
	"net/url"
	"time"
	"vppn/m"
)

type HubPoller struct {
	Globals
	client   *http.Client
	req      *http.Request
	versions [256]int64
	netName  string
}

func NewHubPoller(
	g Globals,
	netName,
	hubURL,
	apiKey string,
) (*HubPoller, error) {
	u, err := url.Parse(hubURL)
	if err != nil {
		return nil, err
	}
	u.Path = "/peer/fetch-state/"

	client := &http.Client{Timeout: 8 * time.Second}

	req := &http.Request{
		Method: http.MethodGet,
		URL:    u,
		Header: http.Header{},
	}
	req.SetBasicAuth("", apiKey)

	return &HubPoller{
		Globals: g,
		client:  client,
		req:     req,
		netName: netName,
	}, nil
}

func (hp *HubPoller) logf(s string, args ...any) {
	log.Printf("[HubPoller] "+s, args...)
}

func (hp *HubPoller) Run() {
	state, err := loadNetworkState(hp.netName)
	if err != nil {
		hp.logf("Failed to load network state: %v", err)
		hp.logf("Polling hub...")
		hp.pollHub()
	} else {
		hp.applyNetworkState(state)
	}

	for range time.Tick(64 * time.Second) {
		hp.pollHub()
	}
}

func (hp *HubPoller) pollHub() {
	var state m.NetworkState

	resp, err := hp.client.Do(hp.req)
	if err != nil {
		hp.logf("Failed to fetch peer state: %v", err)
		return
	}
	body, err := io.ReadAll(resp.Body)
	_ = resp.Body.Close()
	if err != nil {
		hp.logf("Failed to read body from hub: %v", err)
		return
	}

	if err := json.Unmarshal(body, &state); err != nil {
		hp.logf("Failed to unmarshal response from hub: %v\n%s", err, body)
		return
	}

	if err := storeNetworkState(hp.netName, state); err != nil {
		hp.logf("Failed to store network state: %v", err)
	}

	hp.applyNetworkState(state)
}

func (hp *HubPoller) applyNetworkState(state m.NetworkState) {
	for i, peer := range state.Peers {
		if i == int(hp.LocalPeerIP) {
			continue
		}

		if peer != nil && peer.Version == hp.versions[i] {
			continue
		}

		hp.RemotePeers[i].Load().HandlePeerUpdate(peerUpdateMsg{Peer: state.Peers[i]})
		if peer != nil {
			hp.versions[i] = peer.Version
		}
	}
}
