package handlers

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

	"crowdsec-dashy/internal/crowdsec"
)

// HubHandler manages the hub page (collections, parsers, scenarios, postoverflows).
type HubHandler struct {
	deps Deps
}

func NewHubHandler(deps Deps) *HubHandler {
	return &HubHandler{deps: deps}
}

// HubData is passed to the hub template.
type HubData struct {
	PageData
	Tab   string
	Items []crowdsec.HubItem // current tab's items
}

// List renders the hub page for the active tab.
func (h *HubHandler) List(w http.ResponseWriter, r *http.Request) {
	pd := NewPageData(r, "Hub", h.deps.CLIAvailable, h.deps.PollInterval)
	if f := readFlash(r); f.Message != "" {
		pd.Flash = f
	}

	tab := sanitizeTab(r.URL.Query().Get("tab"))
	data := HubData{PageData: pd, Tab: tab}

	if h.deps.CLIAvailable {
		ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
		defer cancel()

		var err error
		switch tab {
		case "collections":
			data.Items, err = h.deps.CLI.ListCollections(ctx)
		case "parsers":
			data.Items, err = h.deps.CLI.ListParsers(ctx)
		case "scenarios":
			data.Items, err = h.deps.CLI.ListScenarios(ctx)
		case "postoverflows":
			data.Items, err = h.deps.CLI.ListPostoverflows(ctx)
		}
		if err != nil {
			data.PageData.Flash = FlashMessage{Type: "error", Message: fmt.Sprintf("cscli error: %v", err)}
		}
	}

	h.deps.Renderer.Render(w, "hub", data)
}

// Install installs a hub item.
func (h *HubHandler) Install(w http.ResponseWriter, r *http.Request) {
	r.Body = http.MaxBytesReader(w, r.Body, 4096)
	if err := r.ParseForm(); err != nil {
		flashRedirect(w, r, "/hub", "error", "invalid form data")
		return
	}
	if !checkCSRF(r) {
		http.Error(w, "forbidden", http.StatusForbidden)
		return
	}

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

	kind := r.FormValue("kind")
	name := r.FormValue("name")
	tab := sanitizeTab(r.FormValue("tab"))

	if err := validateHubKind(kind); err != nil {
		flashRedirect(w, r, hubURL(tab), "error", err.Error())
		return
	}

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

	var err error
	switch kind {
	case "collections":
		err = h.deps.CLI.InstallCollection(ctx, name)
	case "parsers":
		err = h.deps.CLI.InstallParser(ctx, name)
	case "scenarios":
		err = h.deps.CLI.InstallScenario(ctx, name)
	default:
		flashRedirect(w, r, hubURL(tab), "error", "unsupported hub type")
		return
	}

	if err != nil {
		flashRedirect(w, r, hubURL(tab), "error", fmt.Sprintf("install failed: %v", err))
		return
	}

	flashRedirect(w, r, hubURL(tab), "success", fmt.Sprintf("Installed %s", name))
}

// Remove uninstalls a hub item.
func (h *HubHandler) Remove(w http.ResponseWriter, r *http.Request) {
	r.Body = http.MaxBytesReader(w, r.Body, 4096)
	if err := r.ParseForm(); err != nil {
		flashRedirect(w, r, "/hub", "error", "invalid form data")
		return
	}
	if !checkCSRF(r) {
		http.Error(w, "forbidden", http.StatusForbidden)
		return
	}

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

	kind := r.FormValue("kind")
	name := r.FormValue("name")
	tab := sanitizeTab(r.FormValue("tab"))

	if err := validateHubKind(kind); err != nil {
		flashRedirect(w, r, hubURL(tab), "error", err.Error())
		return
	}

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

	var err error
	switch kind {
	case "collections":
		err = h.deps.CLI.RemoveCollection(ctx, name)
	case "parsers":
		err = h.deps.CLI.RemoveParser(ctx, name)
	case "scenarios":
		err = h.deps.CLI.RemoveScenario(ctx, name)
	default:
		flashRedirect(w, r, hubURL(tab), "error", "unsupported hub type")
		return
	}

	if err != nil {
		flashRedirect(w, r, hubURL(tab), "error", fmt.Sprintf("remove failed: %v", err))
		return
	}

	flashRedirect(w, r, hubURL(tab), "success", fmt.Sprintf("Removed %s", name))
}

// Update runs cscli hub update + upgrade.
func (h *HubHandler) Update(w http.ResponseWriter, r *http.Request) {
	r.Body = http.MaxBytesReader(w, r.Body, 1024)
	if err := r.ParseForm(); err != nil {
		flashRedirect(w, r, "/hub", "error", "invalid form data")
		return
	}
	if !checkCSRF(r) {
		http.Error(w, "forbidden", http.StatusForbidden)
		return
	}

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

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

	if err := h.deps.CLI.HubUpdate(ctx); err != nil {
		flashRedirect(w, r, "/hub", "error", fmt.Sprintf("hub update failed: %v", err))
		return
	}

	flashRedirect(w, r, "/hub", "success", "Hub updated and upgraded successfully")
}

func sanitizeTab(tab string) string {
	switch tab {
	case "collections", "parsers", "scenarios", "postoverflows":
		return tab
	}
	return "collections"
}

func hubURL(tab string) string {
	return "/hub?tab=" + tab
}

func validateHubKind(kind string) error {
	switch kind {
	case "collections", "parsers", "scenarios", "postoverflows":
		return nil
	}
	return fmt.Errorf("invalid hub kind: %q", kind)
}
