package api

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path"
	"strings"
	"time"

	"srs.epita.fr/fic-server/admin/pki"
	"srs.epita.fr/fic-server/libfic"

	"github.com/gin-gonic/gin"
)

var TimestampCheck = "submissions"

func declareHealthRoutes(router *gin.RouterGroup) {
	router.GET("/timestamps.json", func(c *gin.Context) {
		stat, err := os.Stat(TimestampCheck)
		if err != nil {
			c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": fmt.Sprintf("timestamp.json: %s", err.Error())})
			return
		}
		now := time.Now().UTC()
		c.JSON(http.StatusOK, gin.H{
			"frontend": stat.ModTime().UTC(),
			"backend":  now,
			"diffFB":   now.Sub(stat.ModTime()),
		})
	})
	router.GET("/health.json", GetHealth)
	router.GET("/submissions-stats.json", GetSubmissionsStats)
	router.GET("/validations-stats.json", GetValidationsStats)
}

type healthFileReport struct {
	IdTeam string `json:"id_team,omitempty"`
	Path   string `json:"path"`
	Error  string `json:"error"`
}

func getHealth(pathname string) (ret []healthFileReport) {
	if ds, err := ioutil.ReadDir(pathname); err != nil {
		ret = append(ret, healthFileReport{
			Path:  strings.TrimPrefix(pathname, TimestampCheck),
			Error: fmt.Sprintf("unable to ReadDir: %s", err),
		})
		return
	} else {
		for _, d := range ds {
			p := path.Join(pathname, d.Name())
			if d.IsDir() && d.Name() != ".tmp" && d.Mode()&os.ModeSymlink == 0 {
				ret = append(ret, getHealth(p)...)
			} else if !d.IsDir() && d.Mode()&os.ModeSymlink == 0 && time.Since(d.ModTime()) > 2*time.Second {
				teamDir := strings.TrimPrefix(pathname, TimestampCheck)
				idteam, _ := pki.GetAssociation(path.Join(TeamsDir, teamDir))
				ret = append(ret, healthFileReport{
					IdTeam: idteam,
					Path:   path.Join(teamDir, d.Name()),
					Error:  "existing untreated file",
				})
			}
		}
		return
	}
}

func GetHealth(c *gin.Context) {
	if _, err := os.Stat(TimestampCheck); err != nil {
		c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": fmt.Sprintf("health.json: %s", err.Error())})
		return
	}

	c.JSON(http.StatusOK, getHealth(TimestampCheck))
}

type SubmissionsStats struct {
	NbSubmissionLastMinute  uint `json:"nbsubminute"`
	NbSubmissionLast5Minute uint `json:"nbsub5minute"`
	NbSubmissionLastQuarter uint `json:"nbsubquarter"`
	NbSubmissionLastHour    uint `json:"nbsubhour"`
	NbSubmissionLastDay     uint `json:"nbsubday"`
}

func calcSubmissionsStats(tries []time.Time) (stats SubmissionsStats) {
	lastMinute := time.Now().Add(-1 * time.Minute)
	last5Minute := time.Now().Add(-5 * time.Minute)
	lastQuarter := time.Now().Add(-15 * time.Minute)
	lastHour := time.Now().Add(-1 * time.Hour)
	lastDay := time.Now().Add(-24 * time.Hour)

	for _, t := range tries {
		if lastMinute.Before(t) {
			stats.NbSubmissionLastMinute += 1
			stats.NbSubmissionLast5Minute += 1
			stats.NbSubmissionLastQuarter += 1
			stats.NbSubmissionLastHour += 1
			stats.NbSubmissionLastDay += 1
		} else if last5Minute.Before(t) {
			stats.NbSubmissionLast5Minute += 1
			stats.NbSubmissionLastQuarter += 1
			stats.NbSubmissionLastHour += 1
			stats.NbSubmissionLastDay += 1
		} else if lastQuarter.Before(t) {
			stats.NbSubmissionLastQuarter += 1
			stats.NbSubmissionLastHour += 1
			stats.NbSubmissionLastDay += 1
		} else if lastHour.Before(t) {
			stats.NbSubmissionLastHour += 1
			stats.NbSubmissionLastDay += 1
		} else if lastDay.Before(t) {
			stats.NbSubmissionLastDay += 1
		}
	}

	return
}

func GetSubmissionsStats(c *gin.Context) {
	tries, err := fic.GetTries(nil, nil)
	if err != nil {
		log.Println("Unable to GetTries:", err.Error())
		c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieves tries."})
		return
	}

	c.JSON(http.StatusOK, calcSubmissionsStats(tries))
}

func GetValidationsStats(c *gin.Context) {
	tries, err := fic.GetValidations(nil, nil)
	if err != nil {
		log.Println("Unable to GetTries:", err.Error())
		c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieves tries."})
		return
	}

	c.JSON(http.StatusOK, calcSubmissionsStats(tries))
}
