package api

import (
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"log"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/cynfo/rmm-backend/db"
	"github.com/cynfo/rmm-backend/models"
	"github.com/cynfo/rmm-backend/ws"
)

type Handler struct {
	db        *db.Database
	scheduler *TaskScheduler
}

func NewHandler(database *db.Database) *Handler {
	return &Handler{db: database}
}

// SetupRoutes - Alle API-Routes registrieren
func (h *Handler) SetupRoutes(mux *http.ServeMux, hub *ws.Hub) {
	// Bestehende Agent-Routes
	mux.HandleFunc("POST /api/v1/agent/register", h.registerAgent)
	mux.HandleFunc("POST /api/v1/agent/heartbeat", h.heartbeat)
	mux.HandleFunc("POST /api/v1/agents", h.preRegisterAgent)
	mux.HandleFunc("GET /api/v1/agents", h.listAgents)
	mux.HandleFunc("GET /api/v1/agents/events", h.getAllAgentEvents)
	mux.HandleFunc("GET /api/v1/agents/{id}", h.getAgent)
	mux.HandleFunc("GET /api/v1/agents/{id}/events", h.getAgentEvents)
	mux.HandleFunc("GET /api/v1/agents/{id}/system", h.getSystemData)
	mux.HandleFunc("DELETE /api/v1/agents/{id}", h.deleteAgent)
	
	// Customer-Routes
	mux.HandleFunc("GET /api/v1/customers", h.listCustomers)
	mux.HandleFunc("POST /api/v1/customers", h.createCustomer)
	mux.HandleFunc("GET /api/v1/customers/{id}", h.getCustomer)
	mux.HandleFunc("PUT /api/v1/customers/{id}", h.updateCustomer)
	mux.HandleFunc("DELETE /api/v1/customers/{id}", h.deleteCustomer)
	mux.HandleFunc("GET /api/v1/customers/{id}/agents", h.getCustomerAgents)

	// Agent-Customer Zuordnung
	mux.HandleFunc("PUT /api/v1/agents/{id}/customer", h.assignAgentCustomer)
	mux.HandleFunc("DELETE /api/v1/agents/{id}/customer", h.unassignAgentCustomer)

	// User-Routes
	mux.HandleFunc("GET /api/v1/users", h.listUsers)
	mux.HandleFunc("POST /api/v1/users", h.createUser)
	mux.HandleFunc("PUT /api/v1/users/{id}/password", h.changePassword)
	mux.HandleFunc("DELETE /api/v1/users/{id}", h.deleteUser)

	// API-Key Routes
	mux.HandleFunc("GET /api/v1/apikeys", h.listAPIKeys)
	mux.HandleFunc("POST /api/v1/apikeys", h.createAPIKey)
	mux.HandleFunc("DELETE /api/v1/apikeys/{id}", h.deleteAPIKey)
	mux.HandleFunc("GET /api/v1/customers/{id}/apikeys", h.listCustomerAPIKeys)

	// System Settings
	mux.HandleFunc("GET /api/v1/settings", h.getSettings)
	mux.HandleFunc("PUT /api/v1/settings", h.updateSettings)

	// Audit-Log
	mux.HandleFunc("GET /api/v1/audit", h.getAuditLogs)

	// Firmware/Agent-Update Routes
	mux.HandleFunc("GET /api/v1/firmware", h.getFirmwareInfo)
	mux.HandleFunc("POST /api/v1/firmware/upload", h.uploadFirmware)
	mux.HandleFunc("GET /api/v1/firmware/download", h.downloadFirmware)
	mux.HandleFunc("POST /api/v1/agents/{id}/agent-update", h.pushAgentUpdate(hub))
	mux.HandleFunc("POST /api/v1/agents/{id}/request-update", h.requestUpdate)
	mux.HandleFunc("DELETE /api/v1/agents/{id}/request-update", h.cancelUpdate)
	mux.HandleFunc("POST /api/v1/agents/request-update-all", h.requestUpdateAll)

	// Installer Routes
	mux.HandleFunc("GET /api/v1/installer", h.getInstallerInfo)
	mux.HandleFunc("POST /api/v1/installer/upload", h.uploadInstaller)
	mux.HandleFunc("GET /api/v1/installer/download", h.downloadInstaller)

	// Task-Routes
	mux.HandleFunc("GET /api/v1/tasks", h.listTasks)
	mux.HandleFunc("GET /api/v1/tasks/{id}", h.getTask)
	mux.HandleFunc("POST /api/v1/tasks", h.createTask(hub))
	mux.HandleFunc("DELETE /api/v1/tasks/{id}", h.deleteTask)
	mux.HandleFunc("POST /api/v1/tasks/{id}/cancel", h.cancelTask)
	mux.HandleFunc("PUT /api/v1/tasks/{id}", h.updateTask)
	mux.HandleFunc("PUT /api/v1/tasks/{id}/toggle", h.toggleTask)
	mux.HandleFunc("POST /api/v1/tasks/{id}/run", h.runTaskNow())

	// WebSocket und Tunnel-Routes
	h.setupTunnelRoutes(mux, hub)
}

func generateID() string {
	b := make([]byte, 16)
	rand.Read(b)
	return hex.EncodeToString(b)
}

func writeJSON(w http.ResponseWriter, status int, v interface{}) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)
	json.NewEncoder(w).Encode(v)
}

func writeError(w http.ResponseWriter, status int, msg string) {
	writeJSON(w, status, map[string]string{"error": msg})
}

// POST /api/v1/agent/register
func (h *Handler) registerAgent(w http.ResponseWriter, r *http.Request) {
	var req models.RegisterRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeError(w, http.StatusBadRequest, "Ungueltige Anfrage")
		return
	}

	if req.Name == "" || req.Hostname == "" {
		writeError(w, http.StatusBadRequest, "Name und Hostname sind erforderlich")
		return
	}

	// Agent-ID bestimmen: vom Agent mitgeliefert > nach Hostname suchen > neu generieren
	agentID := req.AgentID
	isExisting := false

	if agentID != "" {
		// Pruefen ob die ID schon in der DB existiert
		if existing, _, err := h.db.GetAgent(agentID); err == nil && existing != nil {
			isExisting = true
		}
	}

	if agentID == "" {
		// Nach pre-registriertem Agent mit gleichem Namen suchen (hostname="(ausstehend)")
		// Kein hostname-basiertes Matching mehr — gleiche Hostnamen auf verschiedenen Maschinen
		// (z.B. zwei "PVE-REP" bei verschiedenen Kunden) wuerden sonst dieselbe ID bekommen
		if existing, err := h.db.GetAgentByName(req.Name); err == nil && existing != nil {
			if existing.Hostname == "(ausstehend)" {
				agentID = existing.ID
				isExisting = true
				log.Printf("Pre-registrierten Agent gefunden: %s (%s)", existing.Name, existing.ID)
			}
		}
	}

	if agentID == "" {
		agentID = generateID()
	}

	platform := req.Platform
	if platform == "" {
		platform = "freebsd" // Default fuer bestehende OPNsense Agents
	}

	agent := &models.Agent{
		ID:              agentID,
		Name:            req.Name,
		Hostname:        req.Hostname,
		IP:              req.IP,
		OPNsenseVersion: req.OPNsenseVersion,
		AgentVersion:    req.AgentVersion,
		Platform:        platform,
		RegisteredAt:    time.Now().UTC(),
	}

	if err := h.db.RegisterAgent(agent); err != nil {
		log.Printf("Fehler bei Registrierung: %v", err)
		writeError(w, http.StatusInternalServerError, "Registrierung fehlgeschlagen")
		return
	}

	action := "registriert"
	if isExisting {
		action = "re-registriert"
	}
	log.Printf("Agent %s: %s (%s)", action, agent.Name, agent.ID)
	writeJSON(w, http.StatusCreated, models.RegisterResponse{
		ID:      agent.ID,
		Message: "Agent erfolgreich registriert",
	})
}

// POST /api/v1/agents - Pre-Registration einer Firewall
func (h *Handler) preRegisterAgent(w http.ResponseWriter, r *http.Request) {
	var req struct {
		Name       string `json:"name"`
		CustomerID *int   `json:"customer_id,omitempty"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeError(w, http.StatusBadRequest, "Ungueltige Anfrage")
		return
	}

	if req.Name == "" {
		writeError(w, http.StatusBadRequest, "Name ist erforderlich")
		return
	}

	agentID := generateID()
	agent := &models.Agent{
		ID:           agentID,
		Name:         req.Name,
		Hostname:     "(ausstehend)",
		IP:           "",
		RegisteredAt: time.Now().UTC(),
	}

	if err := h.db.RegisterAgent(agent); err != nil {
		log.Printf("Fehler bei Pre-Registration: %v", err)
		writeError(w, http.StatusInternalServerError, "Pre-Registration fehlgeschlagen")
		return
	}

	// Optionale Kundenzuordnung
	if req.CustomerID != nil {
		h.db.AssignAgentCustomer(agentID, *req.CustomerID)
	}

	log.Printf("Agent pre-registriert: %s (%s)", req.Name, agentID)
	h.auditLog(r, "agent.create", "agent", agentID, req.Name, "")
	writeJSON(w, http.StatusCreated, map[string]interface{}{
		"id":      agentID,
		"name":    req.Name,
		"message": "Firewall erfolgreich pre-registriert",
	})
}

// POST /api/v1/agent/heartbeat
func (h *Handler) heartbeat(w http.ResponseWriter, r *http.Request) {
	var req models.HeartbeatRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeError(w, http.StatusBadRequest, "Ungueltige Anfrage")
		return
	}

	if req.AgentID == "" {
		writeError(w, http.StatusBadRequest, "agent_id erforderlich")
		return
	}

	// Agent-Version VOR Update lesen (fuer Update-Flag Logik)
	prevAgentVersion := h.db.GetAgentVersionFromDB(req.AgentID)

	// Agent-Version aktualisieren
	if req.AgentVersion != "" {
		h.db.UpdateAgentVersion(req.AgentID, req.AgentVersion)
	}

	// Genutzten API-Key speichern (aus Middleware-Kontext)
	if keyInfo := GetAPIKeyFromContext(r); keyInfo != nil {
		h.db.UpdateAgentAPIKey(req.AgentID, keyInfo.Key)
	}

	if err := h.db.SaveSystemDataRaw(req.AgentID, req.SystemData); err != nil {
		if strings.Contains(err.Error(), "nicht gefunden") {
			writeError(w, http.StatusNotFound, "Agent nicht gefunden")
			return
		}
		log.Printf("Fehler bei Heartbeat: %v", err)
		writeError(w, http.StatusInternalServerError, "Heartbeat fehlgeschlagen")
		return
	}

	resp := models.HeartbeatResponse{Message: "OK"}

	// Pruefen ob Update ansteht — Plattform aus Heartbeat-Request verwenden
	if h.db.IsUpdateRequested(req.AgentID) {
		platform := req.Platform
		if platform == "" {
			platform = "freebsd" // Default fuer OPNsense Agents ohne Platform-Feld
		}
		version, hash, _, err := h.db.GetFirmwareInfo(platform)
		if err != nil {
			// Keine Firmware vorhanden — Flag sinnlos, loeschen
			h.db.ClearUpdateRequest(req.AgentID)
		} else if version == req.AgentVersion {
			// Agent hat genau die Firmware-Version — fertig
			h.db.ClearUpdateRequest(req.AgentID)
		} else {
			// Pruefen ob Agent-Version sich seit Flag-Setzung geaendert hat
			// (z.B. manuelles Update auf andere Version)
			if prevAgentVersion != "" && prevAgentVersion != req.AgentVersion {
				// Version hat sich geaendert — Update war erfolgreich (manuell oder anderweitig)
				h.db.ClearUpdateRequest(req.AgentID)
			} else {
				resp.UpdateAvailable = true
				resp.UpdateVersion = version
				resp.UpdateHash = hash
			}
		}
	}

	writeJSON(w, http.StatusOK, resp)
}

// GET /api/v1/agents
func (h *Handler) listAgents(w http.ResponseWriter, r *http.Request) {
	agents, err := h.db.GetAgents()
	if err != nil {
		log.Printf("Fehler beim Laden der Agents: %v", err)
		writeError(w, http.StatusInternalServerError, "Fehler beim Laden")
		return
	}
	if agents == nil {
		agents = []models.Agent{}
	}

	// Status fuer jeden Agent berechnen
	var response []models.AgentResponse
	for _, a := range agents {
		response = append(response, models.AgentResponse{
			Agent:  a,
			Status: models.CalculateAgentStatus(a.LastHeartbeat),
		})
	}
	if response == nil {
		response = []models.AgentResponse{}
	}
	writeJSON(w, http.StatusOK, response)
}

// GET /api/v1/agents/{id}
func (h *Handler) getAgent(w http.ResponseWriter, r *http.Request) {
	id := r.PathValue("id")
	agent, sysData, err := h.db.GetAgent(id)
	if err != nil {
		log.Printf("Fehler: %v", err)
		writeError(w, http.StatusInternalServerError, "Fehler beim Laden")
		return
	}
	if agent == nil {
		writeError(w, http.StatusNotFound, "Agent nicht gefunden")
		return
	}

	// system_data als Roh-JSON ausgeben (plattformunabhaengig)
	var rawSD interface{} = sysData
	if sysData != nil && len(sysData.RawJSON) > 0 {
		rawSD = json.RawMessage(sysData.RawJSON)
	}
	result := map[string]interface{}{
		"agent":       agent,
		"status":      models.CalculateAgentStatus(agent.LastHeartbeat),
		"system_data": rawSD,
	}
	writeJSON(w, http.StatusOK, result)
}

// GET /api/v1/agents/{id}/system
func (h *Handler) getSystemData(w http.ResponseWriter, r *http.Request) {
	id := r.PathValue("id")
	data, err := h.db.GetSystemData(id)
	if err != nil {
		log.Printf("Fehler: %v", err)
		writeError(w, http.StatusInternalServerError, "Fehler beim Laden")
		return
	}
	if data == nil {
		writeError(w, http.StatusNotFound, "Keine Systemdaten vorhanden")
		return
	}
	writeJSON(w, http.StatusOK, data)
}

// DELETE /api/v1/agents/{id}
func (h *Handler) deleteAgent(w http.ResponseWriter, r *http.Request) {
	id := r.PathValue("id")
	deleted, err := h.db.DeleteAgent(id)
	if err != nil {
		log.Printf("Fehler beim Loeschen: %v", err)
		writeError(w, http.StatusInternalServerError, "Loeschen fehlgeschlagen")
		return
	}
	if !deleted {
		writeError(w, http.StatusNotFound, "Agent nicht gefunden")
		return
	}
	log.Printf("Agent geloescht: %s", id)
	h.auditLog(r, "agent.delete", "agent", id, id, "")
	writeJSON(w, http.StatusOK, map[string]string{"message": "Agent geloescht"})
}

// GET /api/v1/agents/{id}/events
func (h *Handler) getAgentEvents(w http.ResponseWriter, r *http.Request) {
	agentID := r.PathValue("id")
	eventType := r.URL.Query().Get("type")
	limit := 100
	if l := r.URL.Query().Get("limit"); l != "" {
		if n, err := strconv.Atoi(l); err == nil && n > 0 {
			limit = n
		}
	}

	events, err := h.db.GetAgentEvents(agentID, eventType, limit)
	if err != nil {
		log.Printf("Fehler beim Laden der Events: %v", err)
		writeError(w, http.StatusInternalServerError, "Fehler beim Laden")
		return
	}
	writeJSON(w, http.StatusOK, events)
}

// GET /api/v1/agents/events
func (h *Handler) getAllAgentEvents(w http.ResponseWriter, r *http.Request) {
	eventType := r.URL.Query().Get("type")
	limit := 100
	if l := r.URL.Query().Get("limit"); l != "" {
		if n, err := strconv.Atoi(l); err == nil && n > 0 {
			limit = n
		}
	}

	events, err := h.db.GetAllAgentEvents(eventType, limit)
	if err != nil {
		log.Printf("Fehler beim Laden der Events: %v", err)
		writeError(w, http.StatusInternalServerError, "Fehler beim Laden")
		return
	}
	writeJSON(w, http.StatusOK, events)
}
