package handlers

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"strconv"
	"strings"
	"time"

	"crowdsec-dashy/internal/crowdsec"
)

// AlertsHandler manages the alerts page and its POST actions.
type AlertsHandler struct {
	deps Deps
}

func NewAlertsHandler(deps Deps) *AlertsHandler {
	return &AlertsHandler{deps: deps}
}

const alertsPerPage = 50

// AlertsData is passed to the alerts template.
type AlertsData struct {
	PageData
	Alerts      []crowdsec.Alert
	Filter      crowdsec.AlertFilter
	Page        int
	HasNext     bool
	ShowUpdates bool
}

// List renders the paginated alerts list.
func (h *AlertsHandler) List(w http.ResponseWriter, r *http.Request) {
	ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second)
	defer cancel()

	page, _ := strconv.Atoi(r.URL.Query().Get("page"))
	if page < 1 {
		page = 1
	}
	showUpdates := r.URL.Query().Get("show_updates") == "1"

	filter := crowdsec.AlertFilter{
		Limit:    alertsPerPage + 1,
		Offset:   (page - 1) * alertsPerPage,
		Scenario: r.URL.Query().Get("scenario"),
		IP:       r.URL.Query().Get("ip"),
		Since:    r.URL.Query().Get("since"),
	}

	alerts, err := h.deps.LAPI.ListAlerts(ctx, filter)
	if err != nil {
		log.Printf("alerts: LAPI error: %v", err)
		pd := NewPageData(r, "Alerts", h.deps.CLIAvailable, h.deps.PollInterval)
		if crowdsec.IsForbidden(err) {
			pd = pd.WithFlash("error", "Access denied: re-register machine with admin rights: cscli machines delete crowdsec-dashy && cscli machines add crowdsec-dashy -a")
		} else {
			pd = pd.WithFlash("error", "Failed to fetch alerts from LAPI.")
		}
		h.deps.Renderer.Render(w, "alerts", AlertsData{PageData: pd})
		return
	}

	hasNext := len(alerts) > alertsPerPage
	if hasNext {
		alerts = alerts[:alertsPerPage]
	}

	pd := NewPageData(r, "Alerts", h.deps.CLIAvailable, h.deps.PollInterval)
	if f := readFlash(r); f.Message != "" {
		pd.Flash = f
	}

	h.deps.Renderer.Render(w, "alerts", AlertsData{
		PageData:    pd,
		Alerts:      alerts,
		Filter:      filter,
		Page:        page,
		HasNext:     hasNext,
		ShowUpdates: showUpdates,
	})
}

// Delete processes alert deletion (POST; `id` field may repeat for bulk).
func (h *AlertsHandler) Delete(w http.ResponseWriter, r *http.Request) {
	r.Body = http.MaxBytesReader(w, r.Body, 4096)
	if err := r.ParseForm(); err != nil {
		flashRedirect(w, r, "/alerts", "error", "invalid form data")
		return
	}
	if !checkCSRF(r) {
		http.Error(w, "forbidden", http.StatusForbidden)
		return
	}

	ids := r.Form["id"]
	if len(ids) == 0 {
		flashRedirect(w, r, "/alerts", "error", "no alert selected")
		return
	}

	ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
	defer cancel()

	var errs []string
	deleted := 0
	for _, s := range ids {
		id, err := strconv.ParseInt(s, 10, 64)
		if err != nil || id <= 0 {
			errs = append(errs, fmt.Sprintf("invalid id: %q", s))
			continue
		}
		if err := h.deps.LAPI.DeleteAlert(ctx, id); err != nil {
			errs = append(errs, fmt.Sprintf("id %d: %v", id, err))
			continue
		}
		deleted++
	}

	if len(errs) > 0 {
		flashRedirect(w, r, "/alerts", "error", strings.Join(errs, "; "))
		return
	}

	flashRedirect(w, r, "/alerts", "success", fmt.Sprintf("%d alert(s) deleted", deleted))
}
