package util

import (
	"flag"
	"fmt"
	"os"
	"sync"
	"time"

	consulapi "github.com/hashicorp/consul/api"
	"github.com/stvp/rollbar"
)

var (
	key                = "service/spectre/leader"
	consulAddress      = "localhost:8500"
	sessionRenewPeriod = "5s"
	c                  *consulapi.Client
	sid                string
	sidMutex           *sync.RWMutex
	isMaster           bool
	isMasterMutex      *sync.RWMutex
)

func IsMaster() bool {
	isMasterMutex.Lock()
	defer isMasterMutex.Unlock()
	return isMaster
}

func SetIsMaster(newIsMaster bool) {
	isMasterMutex.Lock()
	defer isMasterMutex.Unlock()
	isMaster = newIsMaster
}

func SID() string {
	sidMutex.Lock()
	defer sidMutex.Unlock()
	return sid
}

func SetSID(newsid string) {
	sidMutex.Lock()
	defer sidMutex.Unlock()
	sid = newsid
}

func StartAcquireLockRoutine() {
	if os.Getenv("ENVIRONMENT") != "production" {
		consulAddress = "consul.internal.justin.tv"
	}

	c, _ = consulapi.NewClient(&consulapi.Config{
		Address:    consulAddress,
		Datacenter: "sfo01",
	})

	sidMutex = &sync.RWMutex{}
	isMasterMutex = &sync.RWMutex{}

	go refreshSID()
	go acquireLock()
}

func refreshSID() {
	for {
		consulErrors := 0
		response, _, err := c.Session().Create(&consulapi.SessionEntry{
			Name: "spectre-session",
			TTL:  "15s",
		}, nil)
		if err != nil {
			consulErrors += 1
			fmt.Println(err)
		}
		SetSID(response)
		fmt.Printf("Renewing session id %v\n", response)
		err = c.Session().RenewPeriodic(sessionRenewPeriod, SID(), nil, nil)
		fmt.Printf("Session id %v expired\n", response)
		if err != nil {
			consulErrors += 1
			fmt.Println(err)
		}

		Stats.IncrByUnsampled("consul_errors", consulErrors)
		SetIsMaster(false)
	}
}

func acquireLock() {
	for {
		if !IsMaster() && SID() != "" {
			gotLock, _, err := c.KV().Acquire(&consulapi.KVPair{
				Key:     key,
				Session: SID(),
				Value:   []byte(hostport()),
			}, nil)
			// fmt.Printf("Tried to acquire lock with session id %v. %v . Waiting for 5 seconds...\n", SID(), IsMaster())
			if err != nil {
				fmt.Println(err)
				gotLock = false
			}

			SetIsMaster(gotLock)
		}
		time.Sleep(5 * time.Second)
	}

}

func hostport() string {
	hostname, err := os.Hostname()
	if err != nil {
		rollbar.ErrorWithStack(rollbar.WARN, err, rollbar.BuildStack(0))
	}

	port := ":80"
	if f := flag.Lookup("bind"); f != nil {
		port = f.Value.String()
	}
	return fmt.Sprintf("%v%v", hostname, port)
}
