package handlers

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

	"crowdsec-dashy/internal/crowdsec"
)

// BouncersHandler manages the bouncers page and its POST actions.
type BouncersHandler struct {
	deps Deps
}

func NewBouncersHandler(deps Deps) *BouncersHandler {
	return &BouncersHandler{deps: deps}
}

// BouncersData is passed to the bouncers template.
type BouncersData struct {
	PageData
	Bouncers   []crowdsec.Bouncer
	NewBouncer *crowdsec.AddBouncerResult // set immediately after add; shown exactly once
}

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

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

	h.deps.Renderer.Render(w, "bouncers", BouncersData{PageData: pd, Bouncers: bouncers})
}

// Add registers a new bouncer and renders the API key reveal page (no redirect).
// The key is shown exactly once in the POST response and never stored by the UI.
func (h *BouncersHandler) Add(w http.ResponseWriter, r *http.Request) {
	r.Body = http.MaxBytesReader(w, r.Body, 4096)
	if err := r.ParseForm(); err != nil {
		flashRedirect(w, r, "/bouncers", "error", "invalid form data")
		return
	}
	if !checkCSRF(r) {
		http.Error(w, "forbidden", http.StatusForbidden)
		return
	}

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

	name := r.FormValue("name")
	if ok, _ := matchName(name); !ok {
		flashRedirect(w, r, "/bouncers", "error", "invalid name: use 1-64 alphanumeric/dash/underscore characters")
		return
	}

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

	result, err := h.deps.CLI.AddBouncer(ctx, name)
	if err != nil {
		flashRedirect(w, r, "/bouncers", "error", fmt.Sprintf("failed to add bouncer: %v", err))
		return
	}

	bouncers, _ := h.deps.CLI.ListBouncers(ctx)

	pd := NewPageData(r, "Bouncers", h.deps.CLIAvailable, h.deps.PollInterval)
	h.deps.Renderer.Render(w, "bouncers", BouncersData{
		PageData:   pd,
		Bouncers:   bouncers,
		NewBouncer: result,
	})
}

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

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

	name := r.FormValue("name")
	if ok, _ := matchName(name); !ok {
		flashRedirect(w, r, "/bouncers", "error", "invalid bouncer name")
		return
	}

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

	if err := h.deps.CLI.DeleteBouncer(ctx, name); err != nil {
		flashRedirect(w, r, "/bouncers", "error", fmt.Sprintf("failed to delete bouncer: %v", err))
		return
	}

	flashRedirect(w, r, "/bouncers", "success", fmt.Sprintf("Bouncer %q deleted", name))
}
