package main

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"time"

	rice "github.com/GeertJohan/go.rice"
	"github.com/gorilla/websocket"
	"github.com/ipld/go-car"
	"github.com/libp2p/go-libp2p"
	"github.com/libp2p/go-libp2p-core/peer"
	pubsub "github.com/libp2p/go-libp2p-pubsub"

	"github.com/filecoin-project/lotus/build"
	"github.com/filecoin-project/lotus/lib/blockstore"
)

var topic = "/fil/headnotifs/"

func init() {
	genBytes := build.MaybeGenesis()
	if len(genBytes) == 0 {
		topic = ""
		return
	}

	bs := blockstore.NewTemporary()

	c, err := car.LoadCar(bs, bytes.NewReader(genBytes))
	if err != nil {
		panic(err)
	}
	if len(c.Roots) != 1 {
		panic("expected genesis file to have one root")
	}

	fmt.Printf("Genesis CID: %s\n", c.Roots[0])
	topic = topic + c.Roots[0].String()
}

var upgrader = websocket.Upgrader{
	WriteBufferSize: 1024,
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

func main() {
	if topic == "" {
		fmt.Println("FATAL: No genesis found")
		return
	}

	ctx := context.Background()

	host, err := libp2p.New(
		ctx,
		libp2p.Defaults,
	)
	if err != nil {
		panic(err)
	}
	ps, err := pubsub.NewGossipSub(ctx, host)
	if err != nil {
		panic(err)
	}

	pi, err := build.BuiltinBootstrap()
	if err != nil {
		panic(err)
	}

	if err := host.Connect(ctx, pi[0]); err != nil {
		panic(err)
	}

	http.HandleFunc("/sub", handler(ps))
	http.Handle("/", http.FileServer(rice.MustFindBox("townhall/build").HTTPBox()))

	fmt.Println("listening on http://localhost:2975")

	if err := http.ListenAndServe("0.0.0.0:2975", nil); err != nil {
		panic(err)
	}
}

type update struct {
	From   peer.ID
	Update json.RawMessage
	Time   uint64
}

func handler(ps *pubsub.PubSub) func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Access-Control-Allow-Origin", "*")
		if r.Header.Get("Sec-WebSocket-Protocol") != "" {
			w.Header().Set("Sec-WebSocket-Protocol", r.Header.Get("Sec-WebSocket-Protocol"))
		}

		conn, err := upgrader.Upgrade(w, r, nil)
		if err != nil {
			return
		}

		sub, err := ps.Subscribe(topic)
		if err != nil {
			return
		}

		fmt.Println("new conn")

		for {
			msg, err := sub.Next(r.Context())
			if err != nil {
				return
			}

			//fmt.Println(msg)

			if err := conn.WriteJSON(update{
				From:   peer.ID(msg.From),
				Update: msg.Data,
				Time:   uint64(time.Now().UnixNano() / 1000_000),
			}); err != nil {
				return
			}
		}
	}
}