package ui

import (
	"context"
	"log/slog"
	"net/http"
	"time"

	"github.com/go-chi/chi/v5"
	"nerd-monitor/internal/auth"
	"nerd-monitor/internal/store"
	"nerd-monitor/views"
)

// Handler serves UI pages.
type Handler struct {
	store *store.Store
	auth  *auth.Manager
}

// New creates a new UI handler.
func New(s *store.Store, a *auth.Manager) *Handler {
	return &Handler{
		store: s,
		auth:  a,
	}
}

// Dashboard renders the dashboard page.
func (h *Handler) Dashboard(w http.ResponseWriter, r *http.Request) {
	slog.Debug("Incoming dashboard request", "method", r.Method, "path", r.URL.Path, "remoteAddr", r.RemoteAddr)

	slog.Debug("Rendering dashboard", "agentCount", len(h.store.GetAllAgents()), "staleAgentCount", len(h.getStaleAgents()))
	agents := h.store.GetAllAgents()
	staleAgents := h.getStaleAgents()

	component := views.Dashboard(agents, staleAgents)
	component.Render(context.Background(), w)
	slog.Debug("Dashboard rendered successfully")
}

// AgentDetail renders the agent detail page.
func (h *Handler) AgentDetail(w http.ResponseWriter, r *http.Request) {
	slog.Debug("Incoming agent detail request", "method", r.Method, "path", r.URL.Path, "remoteAddr", r.RemoteAddr)

	agentID := chi.URLParam(r, "id")
	slog.Debug("Retrieving agent detail", "agentID", agentID)
	agent := h.store.GetAgent(agentID)

	if agent == nil {
		slog.Warn("Agent not found for detail view", "agentID", agentID)
		http.NotFound(w, r)
		return
	}

	component := views.AgentDetail(agent)
	component.Render(context.Background(), w)
	slog.Debug("Agent detail rendered successfully", "agentID", agentID)
}

// Login renders the login page.
func (h *Handler) Login(w http.ResponseWriter, r *http.Request) {
	slog.Debug("Incoming login request", "method", r.Method, "path", r.URL.Path, "remoteAddr", r.RemoteAddr)

	if r.Method == http.MethodGet {
		slog.Debug("Rendering login page")
		component := views.LoginPage("")
		component.Render(context.Background(), w)
		return
	}

	if r.Method == http.MethodPost {
		username := r.FormValue("username")
		password := r.FormValue("password")

		slog.Debug("Attempting login", "username", username, "remoteAddr", r.RemoteAddr)
		token, err := h.auth.Login(username, password)
		if err != nil {
			slog.Warn("Login failed - invalid credentials", "username", username, "remoteAddr", r.RemoteAddr, "error", err)
			component := views.LoginPage("Invalid credentials")
			component.Render(context.Background(), w)
			return
		}

		slog.Info("Login successful", "username", username, "remoteAddr", r.RemoteAddr)
		http.SetCookie(w, &http.Cookie{
			Name:     "session_token",
			Value:    token,
			Path:     "/",
			MaxAge:   86400,
			HttpOnly: true,
			SameSite: http.SameSiteLaxMode,
		})

		http.Redirect(w, r, "/", http.StatusSeeOther)
		return
	}

	slog.Warn("Invalid method for login", "method", r.Method)
	http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}

// Logout handles logout.
func (h *Handler) Logout(w http.ResponseWriter, r *http.Request) {
	slog.Debug("Incoming logout request", "method", r.Method, "path", r.URL.Path, "remoteAddr", r.RemoteAddr)

	if r.Method != http.MethodPost {
		slog.Warn("Invalid method for logout", "method", r.Method)
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	cookie, err := r.Cookie("session_token")
	if err == nil {
		slog.Info("Logging out user", "remoteAddr", r.RemoteAddr)
		h.auth.Logout(cookie.Value)
	} else {
		slog.Debug("Logout attempted without valid session", "remoteAddr", r.RemoteAddr)
	}

	http.SetCookie(w, &http.Cookie{
		Name:     "session_token",
		Value:    "",
		Path:     "/",
		MaxAge:   -1,
		HttpOnly: true,
	})

	http.Redirect(w, r, "/login", http.StatusSeeOther)
}

// RemoveStaleAgents handles bulk removal of stale agents.
func (h *Handler) RemoveStaleAgents(w http.ResponseWriter, r *http.Request) {
	slog.Debug("Incoming remove stale agents request", "method", r.Method, "path", r.URL.Path, "remoteAddr", r.RemoteAddr)

	if r.Method != http.MethodPost {
		slog.Warn("Invalid method for remove stale agents", "method", r.Method)
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	agentIDs := r.Form["agent_ids"]
	slog.Info("Removing stale agents", "count", len(agentIDs), "agentIDs", agentIDs)
	for _, id := range agentIDs {
		h.store.DeleteAgent(id)
	}

	slog.Info("Stale agents removed successfully", "count", len(agentIDs))
	http.Redirect(w, r, "/", http.StatusSeeOther)
}

// DeleteAgent handles single agent deletion.
func (h *Handler) DeleteAgent(w http.ResponseWriter, r *http.Request) {
	slog.Debug("Incoming delete agent request", "method", r.Method, "path", r.URL.Path, "remoteAddr", r.RemoteAddr)

	if r.Method != http.MethodPost {
		slog.Warn("Invalid method for delete agent", "method", r.Method)
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	agentID := chi.URLParam(r, "id")
	slog.Info("Deleting agent", "agentID", agentID)
	h.store.DeleteAgent(agentID)

	slog.Info("Agent deleted successfully", "agentID", agentID)
	http.Redirect(w, r, "/", http.StatusSeeOther)
}

// UpdateAgentHostname handles hostname updates for agents.
func (h *Handler) UpdateAgentHostname(w http.ResponseWriter, r *http.Request) {
	slog.Debug("Incoming update agent hostname request", "method", r.Method, "path", r.URL.Path, "remoteAddr", r.RemoteAddr)

	if r.Method != http.MethodPost {
		slog.Warn("Invalid method for update agent hostname", "method", r.Method)
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	agentID := chi.URLParam(r, "id")
	hostname := r.FormValue("hostname")

	if hostname == "" {
		slog.Warn("Empty hostname provided for update", "agentID", agentID)
		http.Error(w, "Hostname cannot be empty", http.StatusBadRequest)
		return
	}

	slog.Info("Updating agent hostname", "agentID", agentID, "newHostname", hostname)
	err := h.store.UpdateHostname(agentID, hostname)
	if err != nil {
		slog.Warn("Failed to update hostname - agent not found", "agentID", agentID, "error", err)
		http.NotFound(w, r)
		return
	}

	slog.Info("Agent hostname updated successfully", "agentID", agentID, "hostname", hostname)
	http.Redirect(w, r, "/agents/"+agentID, http.StatusSeeOther)
}

// getStaleAgents returns agents that haven't reported in 6 months.
func (h *Handler) getStaleAgents() []*store.AgentStats {
	const staleThreshold = 6 * 30 * 24 * time.Hour
	allAgents := h.store.GetAllAgents()

	var stale []*store.AgentStats
	for _, agent := range allAgents {
		if time.Since(agent.LastSeen) > staleThreshold {
			stale = append(stale, agent)
		}
	}

	return stale
}
