package node

import (
	"context"
	"fmt"
	"log"
	"net"
	"net/http"
	"net/http/httptest"
	"os"
	"testing"
	"time"

	"git.autistici.org/ale/autoradio"
	pb "git.autistici.org/ale/autoradio/proto"
	"github.com/golang/protobuf/proto"
	"go.etcd.io/etcd/clientv3/concurrency"
	"go.etcd.io/etcd/embed"
	"go.etcd.io/etcd/etcdserver/api/v3client"
)

type fakeIcecast struct{}

func (f *fakeIcecast) GetStatus() ([]*pb.IcecastMount, bool) {
	return nil, true
}

func (f *fakeIcecast) Update(_ context.Context, mounts []*pb.Mount, isMaster bool, masterAddr string) error {
	return nil
}

func TestNode(t *testing.T) {
	cfg := embed.NewConfig()
	cfg.Dir = "default.etcd"
	defer os.RemoveAll(cfg.Dir)
	e, err := embed.StartEtcd(cfg)
	if err != nil {
		t.Fatalf("StartEtcd: %v", err)
	}
	defer e.Close()
	<-e.Server.ReadyNotify()

	cli := v3client.New(e.Server)
	session, _ := concurrency.NewSession(cli, concurrency.WithTTL(2))
	ctx, cancel := context.WithCancel(context.Background())

	var nodes []*Node

	for i := 0; i < 2; i++ {
		n, err := New(
			ctx,
			session,
			&fakeIcecast{},
			fmt.Sprintf("node%d", i+1),
			[]net.IP{net.ParseIP("127.0.0.1")},
			net.ParseIP("127.0.0.1"),
			4014,
			"random",
			0, 0,
		)
		if err != nil {
			t.Fatalf("NewNode: %v", err)
		}

		nodes = append(nodes, n)
	}

	go func() {
		time.Sleep(10 * time.Second)
		log.Printf("stopping everything")
		cancel()
	}()

	for _, n := range nodes {
		n.Wait()
	}
}

func TestNode_StatusPage(t *testing.T) {
	cfg := embed.NewConfig()
	cfg.Dir = "default.etcd"
	defer os.RemoveAll(cfg.Dir)
	e, err := embed.StartEtcd(cfg)
	if err != nil {
		t.Fatalf("StartEtcd: %v", err)
	}
	defer e.Close()
	<-e.Server.ReadyNotify()

	cli := v3client.New(e.Server)

	session, _ := concurrency.NewSession(cli, concurrency.WithTTL(2))
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// Create a new Mount
	m := &pb.Mount{
		Path:           "/test.ogg",
		SourceUsername: "user",
		SourcePassword: "pass",
	}
	mdata, _ := proto.Marshal(m)
	_, err = cli.Put(ctx, autoradio.MountPrefix+"test.ogg", string(mdata))
	if err != nil {
		t.Fatalf("Put error: %v", err)
	}

	n, err := New(
		ctx,
		session,
		&fakeIcecast{},
		"node1",
		[]net.IP{net.ParseIP("127.0.0.1")},
		net.ParseIP("127.0.0.1"),
		4014,
		"random",
		0, 0,
	)
	if err != nil {
		t.Fatalf("NewNode: %v", err)
	}

	httpSrv := httptest.NewServer(newHTTPHandler(n, 8080, "example.com"))
	defer httpSrv.Close()

	// Give the Node time to gather its status.
	time.Sleep(1 * time.Second)

	resp, err := http.Get(httpSrv.URL)
	if err != nil {
		t.Fatalf("http.Get(/) error: %v", err)
	}
	resp.Body.Close()
	if resp.StatusCode != 200 {
		t.Fatalf("http.Get(/) error: HTTP: %s", resp.Status)
	}

	// Also check the player page.
	resp, err = http.Get(httpSrv.URL + "/player/test.ogg")
	if err != nil {
		t.Fatalf("http.Get(/player/) error: %v", err)
	}
	resp.Body.Close()
	if resp.StatusCode != 200 {
		t.Fatalf("http.Get(/player/) error: HTTP: %s", resp.Status)
	}

}