package api

import (
	"encoding/json"
	"log"
	"net/http"
	"strconv"
	"time"

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

// GET /api/v1/tasks
func (h *Handler) listTasks(w http.ResponseWriter, r *http.Request) {
	limit := 50
	offset := 0
	agentID := r.URL.Query().Get("agent_id")
	status := r.URL.Query().Get("status")
	action := r.URL.Query().Get("action")

	if l := r.URL.Query().Get("limit"); l != "" {
		if n, err := strconv.Atoi(l); err == nil && n > 0 {
			limit = n
		}
	}
	if o := r.URL.Query().Get("offset"); o != "" {
		if n, err := strconv.Atoi(o); err == nil && n >= 0 {
			offset = n
		}
	}

	tasks, total, err := h.db.GetTasks(limit, offset, agentID, status, action)
	if err != nil {
		log.Printf("Tasks laden fehlgeschlagen: %v", err)
		writeError(w, http.StatusInternalServerError, "Fehler beim Laden")
		return
	}

	writeJSON(w, http.StatusOK, map[string]interface{}{
		"data":  tasks,
		"total": total,
	})
}

// GET /api/v1/tasks/{id}
func (h *Handler) getTask(w http.ResponseWriter, r *http.Request) {
	idStr := r.PathValue("id")
	id, err := strconv.Atoi(idStr)
	if err != nil {
		writeError(w, http.StatusBadRequest, "Ungueltige Task-ID")
		return
	}

	task, err := h.db.GetTask(id)
	if err != nil {
		writeError(w, http.StatusInternalServerError, "Fehler beim Laden")
		return
	}
	if task == nil {
		writeError(w, http.StatusNotFound, "Task nicht gefunden")
		return
	}

	writeJSON(w, http.StatusOK, task)
}

// POST /api/v1/tasks
func (h *Handler) createTask(hub *ws.Hub) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var req struct {
			AgentID            string `json:"agent_id"`
			Name               string `json:"name"`
			Action             string `json:"action"`
			ScheduledAt        string `json:"scheduled_at"`
			ScheduleType       string `json:"schedule_type"`
			ScheduleTime       string `json:"schedule_time"`
			ScheduleWeekday    *int   `json:"schedule_weekday"`
			ScheduleMonthday   *int   `json:"schedule_monthday"`
			Reboot             bool   `json:"reboot"`
			HealthCheck        bool   `json:"health_check"`
			HealthCheckTimeout int    `json:"health_check_timeout"`
			SecurityOnly       bool   `json:"security_only"`
			Script             string `json:"script"`
			ScriptTimeout      int    `json:"script_timeout"`
			WebhookURL         string `json:"webhook_url"`
			Active             *bool  `json:"active"`
		}
		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
			writeError(w, http.StatusBadRequest, "Ungueltige Anfrage")
			return
		}

		if req.AgentID == "" || req.Action == "" {
			writeError(w, http.StatusBadRequest, "agent_id und action sind erforderlich")
			return
		}

		if req.Action != "backup" && req.Action != "update" && req.Action != "zpoolscrub" && req.Action != "script" {
			writeError(w, http.StatusBadRequest, "Ungueltige Aktion (backup, update, zpoolscrub, script)")
			return
		}
		if req.Action == "script" && req.Script == "" {
			writeError(w, http.StatusBadRequest, "Skript-Inhalt darf nicht leer sein")
			return
		}

		agent, _, err := h.db.GetAgent(req.AgentID)
		if err != nil || agent == nil {
			writeError(w, http.StatusNotFound, "Agent nicht gefunden")
			return
		}

		createdBy := "api-key"
		if claims, ok := r.Context().Value(UserContextKey).(*JWTClaims); ok {
			createdBy = claims.Username
		}

		active := true
		if req.Active != nil {
			active = *req.Active
		}

		task, err := h.db.CreateTask(
			req.AgentID, req.Action, req.ScheduledAt, req.WebhookURL, createdBy,
			req.Name, req.ScheduleType, req.ScheduleTime,
			req.ScheduleWeekday, req.ScheduleMonthday,
			req.Reboot, req.HealthCheck, req.HealthCheckTimeout, req.SecurityOnly, active,
			req.Script, req.ScriptTimeout,
		)
		if err != nil {
			log.Printf("Task erstellen fehlgeschlagen: %v", err)
			writeError(w, http.StatusInternalServerError, "Task erstellen fehlgeschlagen: "+err.Error())
			return
		}

		h.auditLog(r, "task.create", "task", strconv.Itoa(task.ID), agent.Name, req.Action)

		// If once and scheduled_at is in the past or now, scheduler picks it up within 30s
		if req.ScheduledAt != "" && req.ScheduleType == "once" {
			if schedTime, err := time.Parse(time.RFC3339, req.ScheduledAt); err == nil && !schedTime.After(time.Now()) {
				// scheduler will pick it up
				_ = schedTime
			}
		}

		writeJSON(w, http.StatusCreated, task)
	}
}

// PUT /api/v1/tasks/{id}/toggle
func (h *Handler) toggleTask(w http.ResponseWriter, r *http.Request) {
	idStr := r.PathValue("id")
	id, err := strconv.Atoi(idStr)
	if err != nil {
		writeError(w, http.StatusBadRequest, "Ungueltige Task-ID")
		return
	}

	task, err := h.db.ToggleTask(id)
	if err != nil {
		writeError(w, http.StatusInternalServerError, "Fehler beim Umschalten")
		return
	}
	if task == nil {
		writeError(w, http.StatusNotFound, "Task nicht gefunden")
		return
	}

	h.auditLog(r, "task.toggle", "task", idStr, "", "")
	writeJSON(w, http.StatusOK, task)
}

// POST /api/v1/tasks/{id}/cancel
func (h *Handler) cancelTask(w http.ResponseWriter, r *http.Request) {
	idStr := r.PathValue("id")
	id, err := strconv.Atoi(idStr)
	if err != nil {
		writeError(w, http.StatusBadRequest, "Ungueltige Task-ID")
		return
	}

	if err := h.db.CancelTask(id); err != nil {
		writeError(w, http.StatusBadRequest, err.Error())
		return
	}

	h.auditLog(r, "task.cancel", "task", idStr, "", "")
	writeJSON(w, http.StatusOK, map[string]string{"message": "Task abgebrochen"})
}

// PUT /api/v1/tasks/{id}
func (h *Handler) updateTask(w http.ResponseWriter, r *http.Request) {
	idStr := r.PathValue("id")
	id, err := strconv.Atoi(idStr)
	if err != nil {
		writeError(w, http.StatusBadRequest, "Ungueltige Task-ID")
		return
	}

	var req struct {
		Name               string `json:"name"`
		ScheduleType       string `json:"schedule_type"`
		ScheduleTime       string `json:"schedule_time"`
		ScheduleWeekday    *int   `json:"schedule_weekday"`
		ScheduleMonthday   *int   `json:"schedule_monthday"`
		Reboot             bool   `json:"reboot"`
		HealthCheck        bool   `json:"health_check"`
		HealthCheckTimeout int    `json:"health_check_timeout"`
		WebhookURL         string `json:"webhook_url"`
		Active             *bool  `json:"active"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeError(w, http.StatusBadRequest, "Ungueltige Anfrage")
		return
	}

	active := true
	if req.Active != nil {
		active = *req.Active
	}
	if req.HealthCheckTimeout <= 0 {
		req.HealthCheckTimeout = 600
	}

	task, err := h.db.UpdateTask(id, req.Name, req.ScheduleType, req.ScheduleTime,
		req.ScheduleWeekday, req.ScheduleMonthday,
		req.Reboot, req.HealthCheck, req.HealthCheckTimeout, req.WebhookURL, active)
	if err != nil {
		writeError(w, http.StatusInternalServerError, "Fehler: "+err.Error())
		return
	}
	if task == nil {
		writeError(w, http.StatusNotFound, "Task nicht gefunden")
		return
	}

	h.auditLog(r, "task.update", "task", idStr, "", "")
	writeJSON(w, http.StatusOK, task)
}

// SetScheduler setzt den Scheduler im Handler (fuer Run-Now)
func (h *Handler) SetScheduler(s *TaskScheduler) {
	h.scheduler = s
}

// POST /api/v1/tasks/{id}/run — Task sofort ausfuehren
func (h *Handler) runTaskNow() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		idStr := r.PathValue("id")
		id, err := strconv.Atoi(idStr)
		if err != nil {
			writeError(w, http.StatusBadRequest, "Ungueltige Task-ID")
			return
		}

		task, err := h.db.GetTask(id)
		if err != nil || task == nil {
			writeError(w, http.StatusNotFound, "Task nicht gefunden")
			return
		}

		if h.scheduler == nil {
			writeError(w, http.StatusInternalServerError, "Scheduler nicht verfuegbar")
			return
		}

		// Sofort im Hintergrund starten
		go h.scheduler.executeTask(*task)

		h.auditLog(r, "task.run_now", "task", idStr, "", task.Action)
		writeJSON(w, http.StatusOK, map[string]string{"message": "Task gestartet"})
	}
}

// DELETE /api/v1/tasks/{id}
func (h *Handler) deleteTask(w http.ResponseWriter, r *http.Request) {
	idStr := r.PathValue("id")
	id, err := strconv.Atoi(idStr)
	if err != nil {
		writeError(w, http.StatusBadRequest, "Ungueltige Task-ID")
		return
	}

	if err := h.db.DeleteTask(id); err != nil {
		writeError(w, http.StatusBadRequest, err.Error())
		return
	}

	h.auditLog(r, "task.delete", "task", idStr, "", "")
	writeJSON(w, http.StatusOK, map[string]string{"message": "Task geloescht"})
}
