package traefik_umami_feeder

import (
	"context"
	"fmt"
	"io"
	"net/http"
	"time"
)

type UmamiEvent struct {
	Website   string `json:"website"`             // Website ID
	Hostname  string `json:"hostname"`            // Name of host
	Language  string `json:"language,omitempty"`  // Language of visitor (ex. "en-US")
	Referrer  string `json:"referrer,omitempty"`  // Referrer URL
	Url       string `json:"url"`                 // Page URL
	Ip        string `json:"ip,omitempty"`        // IP address
	UserAgent string `json:"userAgent,omitempty"` // User agent
	Timestamp int64  `json:"timestamp,omitempty"` // UNIX timestamp in seconds
	//Data     map[string]interface{} `json:"data,omitempty"`   // Additional data for the event
	//Name     string                 `json:"name,omitempty"`   // Event name (for custom events)
	//Screen   string                 `json:"screen,omitempty"` // Screen resolution (ex. "1920x1080")
	//Title    string                 `json:"title,omitempty"`  // Page title
}

type SendBody struct {
	Payload *UmamiEvent `json:"payload"`
	Type    string      `json:"type"`
}

func (h *UmamiFeeder) submitToFeed(req *http.Request, code int) {
	hostname := parseDomainFromHost(req.Host)
	websiteId := getWebsiteId(h, hostname)

	if websiteId == "" {
		h.error("tracking skipped, websiteId is unknown: " + hostname)
		return
	}

	event := &UmamiEvent{
		Hostname:  hostname,
		Language:  parseAcceptLanguage(req.Header.Get("Accept-Language")),
		Referrer:  req.Referer(),
		Url:       req.URL.String(),
		Ip:        extractRemoteIP(req),
		UserAgent: req.Header.Get("User-Agent"),
		Timestamp: time.Now().Unix(),
		Website:   websiteId,
	}

	select {
	case h.queue <- event:
	default:
		h.error("failed to submit event: queue full")
	}
}

func (h *UmamiFeeder) startWorker(ctx context.Context) {
	for {
		err := h.umamiEventFeeder(ctx)
		if err != nil {
			h.error("worker failed: " + err.Error())
		} else {
			return
		}
	}
}

func (h *UmamiFeeder) umamiEventFeeder(ctx context.Context) (err error) {
	defer func() {
		// Recover from panic.
		panicVal := recover()
		if panicVal != nil {
			h.error("panic: " + fmt.Sprint(panicVal))
		}
	}()

	batch := make([]*SendBody, 0, h.batchSize)
	timeout := time.NewTimer(h.batchMaxWait)

	for {
		// Wait for event.
		select {
		case <-ctx.Done():
			h.debug("worker shutting down (canceled)")
			if len(batch) > 0 {
				h.reportEventsToUmami(ctx, batch)
			}
			return nil

		case event := <-h.queue:
			batch = append(batch, &SendBody{Payload: event, Type: "event"})
			if len(batch) >= h.batchSize {
				h.reportEventsToUmami(ctx, batch)
				batch = make([]*SendBody, 0, h.batchSize)
				timeout.Reset(h.batchMaxWait)
			}

		case <-timeout.C:
			if len(batch) > 0 {
				h.reportEventsToUmami(ctx, batch)
				batch = make([]*SendBody, 0, h.batchSize)
			}
			timeout.Reset(h.batchMaxWait)
		}
	}
}

func (h *UmamiFeeder) reportEventsToUmami(ctx context.Context, events []*SendBody) {
	h.debug("reporting %d events", len(events))
	resp, err := sendRequest(ctx, h.umamiHost+"/api/batch", events, nil)
	if err != nil {
		h.error("failed to send tracking: " + err.Error())
		return
	}
	if h.isDebug {
		bodyBytes, _ := io.ReadAll(resp.Body)
		h.debug("%v: %s", resp.Status, string(bodyBytes))
	}
	defer func() {
		_ = resp.Body.Close()
	}()
}
