package middleware

import (
	"bytes"
	"encoding/json"
	"net/http"
	"sync"
	"time"

	. "github.com/mickael-kerjean/filestash/server/common"
)

var telemetry = Telemetry{Data: make([]LogEntry, 0)}

type Telemetry struct {
	Data []LogEntry
	mu   sync.Mutex
}

type LogEntry struct {
	Host       string  `json:"host"`
	Method     string  `json:"method"`
	RequestURI string  `json:"pathname"`
	Proto      string  `json:"proto"`
	Status     int     `json:"status"`
	Scheme     string  `json:"scheme"`
	UserAgent  string  `json:"userAgent"`
	Ip         string  `json:"ip"`
	Referer    string  `json:"referer"`
	Duration   float64 `json:"responseTime"`
	Version    string  `json:"version"`
	Backend    string  `json:"backend"`
	Share      string  `json:"share"`
	License    string  `json:"license"`
	Session    string  `json:"session"`
	RequestID  string  `json:"requestID"`
}

func logger(ctx App, res http.ResponseWriter, req *http.Request) {
	if obj, ok := res.(*ResponseWriter); ok && req.RequestURI != "/about" {
		point := LogEntry{
			Version:    APP_VERSION + "." + BUILD_DATE,
			License:    LICENSE,
			Scheme:     req.URL.Scheme,
			Host:       req.Host,
			Method:     req.Method,
			RequestURI: req.RequestURI,
			Proto:      req.Proto,
			Status:     obj.status,
			UserAgent:  req.Header.Get("User-Agent"),
			Ip:         req.RemoteAddr,
			Referer:    req.Referer(),
			Duration:   float64(time.Now().Sub(obj.start)) / (1000 * 1000),
			Backend: func() string {
				if ctx.Session["type"] == "" {
					return "null"
				}
				return ctx.Session["type"]
			}(),
			Share: func() string {
				if ctx.Share.Id == "" {
					return "null"
				}
				return ctx.Share.Id
			}(),
			Session: func() string {
				if ctx.Session["type"] == "" {
					return "null"
				}
				return GenerateID(ctx.Session)
			}(),
			RequestID: func() string {
				defer func() string {
					if r := recover(); r != nil {
						return "oops"
					}
					return "null"
				}()
				return res.Header().Get("X-Request-ID")
			}(),
		}
		if Config.Get("log.telemetry").Bool() {
			telemetry.Record(point)
		}
		if Config.Get("log.enable").Bool() {
			Log.Stdout("HTTP %3d %3s %6.1fms %s", point.Status, point.Method, point.Duration, limit(point.RequestURI, 200))
		}
	}
}

func limit(input string, maxLength int) string {
	if len(input) > maxLength {
		return input[:maxLength] + "..."
	}
	return input
}

func (this *Telemetry) Record(point LogEntry) {
	this.mu.Lock()
	this.Data = append(this.Data, point)
	this.mu.Unlock()
}

func (this *Telemetry) Flush() {
	if len(this.Data) == 0 {
		return
	}
	this.mu.Lock()
	pts := this.Data
	this.Data = make([]LogEntry, 0)
	this.mu.Unlock()

	body, err := json.Marshal(pts)
	if err != nil {
		return
	}
	r, err := http.NewRequest("POST", "https://downloads.filestash.app/event", bytes.NewReader(body))
	r.Header.Set("Connection", "Close")
	r.Header.Set("Content-Type", "application/json")
	r.Close = true
	if err != nil {
		return
	}
	resp, err := HTTP.Do(r)
	if err != nil {
		return
	}
	resp.Body.Close()
}
