package main

import (
	log "github.com/cihub/seelog"

	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/gorilla/mux"
	"gitlab.com/trantor/trantor/database"
	"gitlab.com/trantor/trantor/storage"
)

const (
	stats_version = 2
)

type handler struct {
	w     http.ResponseWriter
	r     *http.Request
	sess  *Session
	db    *database.DB
	store *storage.Store
}

type StatsGatherer struct {
	db      *database.DB
	store   *storage.Store
	channel chan statsRequest
}

func InitStats(database *database.DB, store *storage.Store) *StatsGatherer {
	sg := new(StatsGatherer)
	sg.channel = make(chan statsRequest, CHAN_SIZE)
	sg.db = database
	sg.store = store

	go sg.worker()
	return sg
}

func (sg StatsGatherer) Gather(function func(handler)) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		log.Info("Query ", r.Method, " ", r.RequestURI)

		var h handler
		h.store = sg.store
		h.db = sg.db.Copy()
		defer h.db.Close()

		h.w = w
		h.r = r
		h.sess = GetSession(r, h.db)

		function(h)

		sg.channel <- statsRequest{time.Now(), mux.Vars(r), h.sess, r}
	}
}

type statsRequest struct {
	date time.Time
	vars map[string]string
	sess *Session
	r    *http.Request
}

func (sg StatsGatherer) worker() {
	db := sg.db.Copy()
	defer db.Close()

	for req := range sg.channel {
		stats := make(map[string]interface{})
		appendFiles(req.r, stats)
		appendMuxVars(req.vars, stats)
		appendUrl(req.r, stats)
		appendSession(req.sess, stats)
		stats["version"] = stats_version
		stats["method"] = req.r.Method
		stats["date"] = req.date
		db.AddStats(stats)
	}
}

func statsHandler(h handler) {
	var data statsData
	data.S = GetStatus(h)
	data.S.Title = "Stats -- " + data.S.Title
	data.S.Stats = true
	data.HVisits = getVisits(hourlyLabel, h.db, database.Hourly_visits)
	data.DVisits = getVisits(dailyLabel, h.db, database.Daily_visits)
	data.MVisits = getVisits(monthlyLabel, h.db, database.Monthly_visits)
	data.HDownloads = getVisits(hourlyLabel, h.db, database.Hourly_downloads)
	data.DDownloads = getVisits(dailyLabel, h.db, database.Daily_downloads)
	data.MDownloads = getVisits(monthlyLabel, h.db, database.Monthly_downloads)

	loadTemplate(h, "stats", data)
}

type statsData struct {
	S          Status
	HVisits    []visitData
	DVisits    []visitData
	MVisits    []visitData
	HDownloads []visitData
	DDownloads []visitData
	MDownloads []visitData
}

type visitData struct {
	Label string
	Count int
}

func hourlyLabel(date time.Time) string {
	return strconv.Itoa(date.Hour() + 1)
}

func dailyLabel(date time.Time) string {
	return strconv.Itoa(date.Day())
}

func monthlyLabel(date time.Time) string {
	return date.Month().String()
}

func getVisits(funcLabel func(time.Time) string, db *database.DB, visitType database.VisitType) []visitData {
	var visits []visitData

	visit, err := db.GetVisits(visitType)
	if err != nil {
		log.Warn("GetVisits error (", visitType, "): ", err)
	}
	for _, v := range visit {
		var elem visitData
		elem.Label = funcLabel(v.Date.UTC())
		elem.Count = v.Count
		visits = append(visits, elem)
	}

	return visits
}

func appendFiles(r *http.Request, stats map[string]interface{}) {
	if r.Method == "POST" && r.MultipartForm != nil {
		files := r.MultipartForm.File
		for key := range files {
			list := make([]string, len(files[key]))
			for i, f := range files[key] {
				list[i] = f.Filename
			}
			stats[key] = list
		}
	}
}

func appendMuxVars(vars map[string]string, stats map[string]interface{}) {
	for key, value := range vars {
		switch {
		case key == "id":
			stats["id"] = value
		case key == "ids":
			var objectIds []string
			ids := strings.Split(value, "/")
			for _, id := range ids {
				objectIds = append(objectIds, id)
			}
			if len(objectIds) > 0 {
				stats["ids"] = objectIds
				stats["id"] = objectIds[0]
			}
		default:
			stats[key] = value
		}
	}
}

func appendUrl(r *http.Request, stats map[string]interface{}) {
	for key, value := range r.URL.Query() {
		stats[key] = value
	}
	stats["host"] = r.Host
	stats["path"] = r.URL.Path
	pattern := strings.Split(r.URL.Path, "/")
	if len(pattern) > 1 && pattern[1] != "" {
		stats["section"] = pattern[1]
	} else {
		stats["section"] = "/"
	}
}

func appendSession(sess *Session, stats map[string]interface{}) {
	stats["session"] = sess.Id()
	if sess.User != "" {
		stats["user"] = sess.User
	}
}