package handlers

import (
	"context"
	"fmt"
	"net/http"
	"regexp"
	"time"

	"crowdsec-dashy/internal/crowdsec"
)

// MachinesHandler manages the machines page and its POST actions.
type MachinesHandler struct {
	deps Deps
}

func NewMachinesHandler(deps Deps) *MachinesHandler {
	return &MachinesHandler{deps: deps}
}

// MachinesData is passed to the machines template.
type MachinesData struct {
	PageData
	Machines []crowdsec.Machine
}

var validMachineID = regexp.MustCompile(`^[a-zA-Z0-9_.\-]{1,128}$`)

// List renders the machines list.
func (h *MachinesHandler) List(w http.ResponseWriter, r *http.Request) {
	pd := NewPageData(r, "Machines", h.deps.CLIAvailable, h.deps.PollInterval)
	if f := readFlash(r); f.Message != "" {
		pd.Flash = f
	}

	var machines []crowdsec.Machine
	if h.deps.CLIAvailable {
		ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second)
		defer cancel()
		var err error
		machines, err = h.deps.CLI.ListMachines(ctx)
		if err != nil {
			pd.Flash = FlashMessage{Type: "error", Message: fmt.Sprintf("cscli error: %v", err)}
		}
	}

	h.deps.Renderer.Render(w, "machines", MachinesData{PageData: pd, Machines: machines})
}

// Delete removes a machine by ID.
func (h *MachinesHandler) Delete(w http.ResponseWriter, r *http.Request) {
	r.Body = http.MaxBytesReader(w, r.Body, 4096)
	if err := r.ParseForm(); err != nil {
		flashRedirect(w, r, "/machines", "error", "invalid form data")
		return
	}
	if !checkCSRF(r) {
		http.Error(w, "forbidden", http.StatusForbidden)
		return
	}

	if !h.deps.CLIAvailable {
		flashRedirect(w, r, "/machines", "error", "cscli not available")
		return
	}

	id := r.FormValue("id")
	if !validMachineID.MatchString(id) {
		flashRedirect(w, r, "/machines", "error", "invalid machine id")
		return
	}

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

	if err := h.deps.CLI.DeleteMachine(ctx, id); err != nil {
		flashRedirect(w, r, "/machines", "error", fmt.Sprintf("failed to delete machine: %v", err))
		return
	}

	flashRedirect(w, r, "/machines", "success", fmt.Sprintf("Machine %q deleted", id))
}

// Validate approves a pending machine registration.
func (h *MachinesHandler) Validate(w http.ResponseWriter, r *http.Request) {
	r.Body = http.MaxBytesReader(w, r.Body, 4096)
	if err := r.ParseForm(); err != nil {
		flashRedirect(w, r, "/machines", "error", "invalid form data")
		return
	}
	if !checkCSRF(r) {
		http.Error(w, "forbidden", http.StatusForbidden)
		return
	}

	if !h.deps.CLIAvailable {
		flashRedirect(w, r, "/machines", "error", "cscli not available")
		return
	}

	id := r.FormValue("id")
	if !validMachineID.MatchString(id) {
		flashRedirect(w, r, "/machines", "error", "invalid machine id")
		return
	}

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

	if err := h.deps.CLI.ValidateMachine(ctx, id); err != nil {
		flashRedirect(w, r, "/machines", "error", fmt.Sprintf("failed to validate machine: %v", err))
		return
	}

	flashRedirect(w, r, "/machines", "success", fmt.Sprintf("Machine %q validated", id))
}
