package main

import (
	"fmt"
	"html/template"
	"net/http"
	"strconv"
	"time"

	"a.yandex-team.ru/kikimr/public/sdk/go/persqueue"
	"a.yandex-team.ru/library/go/core/log"
)

const DefaultItems = 15
const MaxItems = 50
const MaxLastBuckets = 5

// bypass template limitations for functions without args
func templateMaxInt(x int, y int) int {
	if x > y {
		return x
	} else {
		return y
	}
}

func SeqWrapper(seq int) []int {
	r := make([]int, seq)
	for i := range r {
		r[i] = i
	}

	return r
}

// TemplateLenType is interface with length calculation function.
// Right now golang has no generic types, so we could hack this
// with this interface.

type TemplateStringLenType []string
type TemplateHTMLPageDataLenType []HTMLPageData

type TemplateLenType interface {
	CalcLen() int
}

func (t TemplateStringLenType) CalcLen() int {
	return len(t)
}

func (t TemplateHTMLPageDataLenType) CalcLen() int {
	return len(t)
}

func templateCalcSignleLen(item TemplateLenType) int {
	switch item.(type) {
	case TemplateLenType:
		return item.CalcLen()
	default:
		return 0
	}
}

func templateCalcMaxLen(items ...TemplateLenType) int {
	if len(items) != 2 {
		return 0
	}

	x := templateCalcSignleLen(items[0])
	y := templateCalcSignleLen(items[1])

	return templateMaxInt(x, y)
}

var funcMap = template.FuncMap{
	"lockStats":   LockStatsTemplateWrapper,
	"unlockStats": UnlockStatsTemplateWrapper,
	"lockGraph":   LockGraphTemplateWrapper,
	"unlockGraph": UnlockGraphTemplateWrapper,
	"seq":         SeqWrapper,
	"getSucc":     getSucc,
	"getErr":      getErr,
	"getStrByIdx": getStrByIdx,

	"calcMaxLen": templateCalcMaxLen,
}

func CalcTopN(storage *StatStorage, maxN int) []HTMLPageData {
	currDate := time.Now()
	currPart := calcPart(&currDate)

	topN := CreateStatSuperHeap()

	// last 5 buckets
	for i := 0; i < MaxLastBuckets; i++ {
		idx := currPart - i

		if idx < 0 {
			idx += timeBucketsCount
		}

		topK := storage[idx].Copy()

		for {
			k, err := topK.Pop()
			if err != nil {
				break
			}

			topN.Push(k.Key, k.Code, k.Value)

		}
	}

	templateData := make([]HTMLPageData, 0)

	for i := 0; i < maxN; i++ {
		n, err := topN.Pop()

		if err != nil {
			break
		}

		templateData = append(templateData, HTMLPageData{
			Index: i,
			Name:  n.Key,
			Code:  n.Code,
			Value: n.Value,
		})
	}

	return templateData
}

type HTMLPageDataTmpl struct {
	Err     TemplateHTMLPageDataLenType
	Succ    TemplateHTMLPageDataLenType
	Signals *GraphTopStorage
	Host    string
}

func DataHandler(w http.ResponseWriter, r *http.Request, errStorage *StatStorage, succStorage *StatStorage, graphStorage *GraphTopStorage, hostname string) {
	w.Header().Set("Content-Type", "text/html")

	topN := DefaultItems

	if qcnt, ok := r.URL.Query()["cnt"]; ok && len(qcnt) > 0 {
		xcnt, err := strconv.ParseInt(qcnt[0], 10, 32)

		if err == nil && xcnt < MaxItems {
			topN = int(xcnt)
		} else if err == nil {
			topN = MaxItems
		}
	}

	errorsTemplateData := CalcTopN(errStorage, topN)
	succTemplateData := CalcTopN(succStorage, topN)

	pageTemplate, err := template.New("page").Funcs(funcMap).Parse(HTMLPageTempate)
	if err != nil {
		Log.Error("Error parsing page template", log.Error(err))
		http.Error(w, "Error parsing template", http.StatusInternalServerError)
		return
	}

	if err := pageTemplate.ExecuteTemplate(w, "main", &HTMLPageDataTmpl{
		Err: errorsTemplateData, Succ: succTemplateData, Signals: graphStorage, Host: hostname,
	}); err != nil {
		Log.Error("Error rendering template", log.Error(err))
		http.Error(w, "Error rendering template", http.StatusInternalServerError)
	}
}

func BucketsHandler(w http.ResponseWriter, storage *StatStorage) {
	w.Header().Set("Content-Type", "text/html")

	currDate := time.Now()
	currPart := calcPart(&currDate)

	pageTemplate, err := template.New("").Funcs(funcMap).Parse(HTMLBucketTemplate)
	if err != nil {
		Log.Error("Error parsing page template", log.Error(err))
		http.Error(w, "Error parsing template", http.StatusInternalServerError)
		return
	}

	if err := pageTemplate.ExecuteTemplate(w, "main", HTMLBucketData{CurrentPart: currPart, Storage: storage}); err != nil {
		Log.Error("Error rendering template", log.Error(err))
		http.Error(w, "Error rendering template", http.StatusInternalServerError)
	}
}

func UnistatHandler(w http.ResponseWriter, c persqueue.Reader) {
	stats := c.Stat()

	w.Header().Set("Content-Type", "text/json")
	_, err := fmt.Fprintf(w, `[["mem_usage_summ", %d],
["inflight_count_summ", %d],
["wait_ack_count_summ", %d],
["bytes_extracted_summ", %d],
["bytes_read_summ", %d]]`, stats.MemUsage, stats.InflightCount, stats.WaitAckCount, stats.BytesExtracted, stats.BytesRead)

	if err != nil {
		Log.Error("Error writing unistat data", log.Error(err))
		http.Error(w, "Error writing unistat data", http.StatusInternalServerError)
	}

}
