package maintenance

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

	"github.com/pocketbase/dbx"
	"github.com/pocketbase/pocketbase/apis"
	"github.com/pocketbase/pocketbase/core"
)

// APIHandler handles maintenance window API requests
type APIHandler struct {
	app core.App
}

// NewAPIHandler creates a new maintenance API handler
func NewAPIHandler(app core.App) *APIHandler {
	return &APIHandler{app: app}
}

// RegisterRoutes registers maintenance API routes
func (h *APIHandler) RegisterRoutes(se *core.ServeEvent) {
	api := se.Router.Group("/api/beszel/maintenance")
	api.Bind(apis.RequireAuth())

	api.GET("/", h.listMaintenanceWindows)
	api.POST("/", h.createMaintenanceWindow)
	api.GET("/{id}", h.getMaintenanceWindow)
	api.PATCH("/{id}", h.updateMaintenanceWindow)
	api.DELETE("/{id}", h.deleteMaintenanceWindow)
	api.POST("/{id}/cancel", h.cancelMaintenanceWindow)
}

// MaintenanceWindowRequest for create/update
type MaintenanceWindowRequest struct {
	Name              string     `json:"name"`
	Description       string     `json:"description"`
	MonitorID         string     `json:"monitor_id"`
	DomainID          string     `json:"domain_id"`
	StartTime         time.Time  `json:"start_time"`
	EndTime           time.Time  `json:"end_time"`
	Recurring         bool       `json:"recurring"`
	RecurrencePattern string     `json:"recurrence_pattern"`
	SuppressAlerts    bool       `json:"suppress_alerts"`
}

// listMaintenanceWindows lists all maintenance windows for the authenticated user
func (h *APIHandler) listMaintenanceWindows(e *core.RequestEvent) error {
	authRecord := e.Auth
	if authRecord == nil {
		return e.UnauthorizedError("unauthorized", nil)
	}

	records, err := h.app.FindAllRecords("maintenance_windows",
		dbx.NewExp("user = {:user}", dbx.Params{"user": authRecord.Id}),
	)
	if err != nil {
		return e.InternalServerError("failed to fetch maintenance windows", err)
	}

	windows := make([]map[string]interface{}, 0, len(records))
	for _, record := range records {
		windows = append(windows, h.recordToResponse(record))
	}

	return e.JSON(http.StatusOK, windows)
}

// createMaintenanceWindow creates a new maintenance window
func (h *APIHandler) createMaintenanceWindow(e *core.RequestEvent) error {
	authRecord := e.Auth
	if authRecord == nil {
		return e.UnauthorizedError("unauthorized", nil)
	}

	var req MaintenanceWindowRequest
	if err := json.NewDecoder(e.Request.Body).Decode(&req); err != nil {
		return e.BadRequestError("invalid request body", err)
	}

	if req.Name == "" || req.StartTime.IsZero() || req.EndTime.IsZero() {
		return e.BadRequestError("name, start_time, and end_time are required", nil)
	}

	// Verify monitor/domain belongs to user if specified
	if req.MonitorID != "" {
		monitor, err := h.app.FindRecordById("monitors", req.MonitorID)
		if err != nil || monitor.GetString("user") != authRecord.Id {
			return e.BadRequestError("invalid monitor_id", nil)
		}
	}
	if req.DomainID != "" {
		domain, err := h.app.FindRecordById("domains", req.DomainID)
		if err != nil || domain.GetString("user") != authRecord.Id {
			return e.BadRequestError("invalid domain_id", nil)
		}
	}

	collection, err := h.app.FindCollectionByNameOrId("maintenance_windows")
	if err != nil {
		return e.InternalServerError("failed to find collection", err)
	}

	record := core.NewRecord(collection)
	record.Set("name", req.Name)
	record.Set("description", req.Description)
	record.Set("monitor", req.MonitorID)
	record.Set("domain", req.DomainID)
	record.Set("start_time", req.StartTime)
	record.Set("end_time", req.EndTime)
	record.Set("recurring", req.Recurring)
	record.Set("recurrence_pattern", req.RecurrencePattern)
	record.Set("suppress_alerts", req.SuppressAlerts)
	record.Set("status", "scheduled")
	record.Set("user", authRecord.Id)

	if err := h.app.Save(record); err != nil {
		return e.InternalServerError("failed to create maintenance window", err)
	}

	return e.JSON(http.StatusCreated, h.recordToResponse(record))
}

// getMaintenanceWindow gets a single maintenance window
func (h *APIHandler) getMaintenanceWindow(e *core.RequestEvent) error {
	authRecord := e.Auth
	if authRecord == nil {
		return e.UnauthorizedError("unauthorized", nil)
	}

	id := e.Request.PathValue("id")
	record, err := h.app.FindRecordById("maintenance_windows", id)
	if err != nil {
		return e.NotFoundError("maintenance window not found", err)
	}

	if record.GetString("user") != authRecord.Id {
		return e.ForbiddenError("not authorized", nil)
	}

	return e.JSON(http.StatusOK, h.recordToResponse(record))
}

// updateMaintenanceWindow updates a maintenance window
func (h *APIHandler) updateMaintenanceWindow(e *core.RequestEvent) error {
	authRecord := e.Auth
	if authRecord == nil {
		return e.UnauthorizedError("unauthorized", nil)
	}

	id := e.Request.PathValue("id")
	record, err := h.app.FindRecordById("maintenance_windows", id)
	if err != nil {
		return e.NotFoundError("maintenance window not found", err)
	}

	if record.GetString("user") != authRecord.Id {
		return e.ForbiddenError("not authorized", nil)
	}

	// Only allow updates if not already completed or cancelled
	status := record.GetString("status")
	if status == "completed" || status == "cancelled" {
		return e.BadRequestError("cannot update completed or cancelled maintenance window", nil)
	}

	var req MaintenanceWindowRequest
	if err := json.NewDecoder(e.Request.Body).Decode(&req); err != nil {
		return e.BadRequestError("invalid request body", err)
	}

	if req.Name != "" {
		record.Set("name", req.Name)
	}
	if req.Description != "" {
		record.Set("description", req.Description)
	}
	if !req.StartTime.IsZero() {
		record.Set("start_time", req.StartTime)
	}
	if !req.EndTime.IsZero() {
		record.Set("end_time", req.EndTime)
	}

	if err := h.app.Save(record); err != nil {
		return e.InternalServerError("failed to update maintenance window", err)
	}

	return e.JSON(http.StatusOK, h.recordToResponse(record))
}

// deleteMaintenanceWindow deletes a maintenance window
func (h *APIHandler) deleteMaintenanceWindow(e *core.RequestEvent) error {
	authRecord := e.Auth
	if authRecord == nil {
		return e.UnauthorizedError("unauthorized", nil)
	}

	id := e.Request.PathValue("id")
	record, err := h.app.FindRecordById("maintenance_windows", id)
	if err != nil {
		return e.NotFoundError("maintenance window not found", err)
	}

	if record.GetString("user") != authRecord.Id {
		return e.ForbiddenError("not authorized", nil)
	}

	if err := h.app.Delete(record); err != nil {
		return e.InternalServerError("failed to delete maintenance window", err)
	}

	return e.NoContent(http.StatusNoContent)
}

// cancelMaintenanceWindow cancels a maintenance window
func (h *APIHandler) cancelMaintenanceWindow(e *core.RequestEvent) error {
	authRecord := e.Auth
	if authRecord == nil {
		return e.UnauthorizedError("unauthorized", nil)
	}

	id := e.Request.PathValue("id")
	record, err := h.app.FindRecordById("maintenance_windows", id)
	if err != nil {
		return e.NotFoundError("maintenance window not found", err)
	}

	if record.GetString("user") != authRecord.Id {
		return e.ForbiddenError("not authorized", nil)
	}

	status := record.GetString("status")
	if status == "completed" || status == "cancelled" {
		return e.BadRequestError("cannot cancel completed or already cancelled maintenance window", nil)
	}

	record.Set("status", "cancelled")
	if err := h.app.Save(record); err != nil {
		return e.InternalServerError("failed to cancel maintenance window", err)
	}

	return e.JSON(http.StatusOK, h.recordToResponse(record))
}

// recordToResponse converts a record to a response map
func (h *APIHandler) recordToResponse(record *core.Record) map[string]interface{} {
	return map[string]interface{}{
		"id":                 record.Id,
		"name":               record.GetString("name"),
		"description":        record.GetString("description"),
		"monitor_id":         record.GetString("monitor"),
		"domain_id":          record.GetString("domain"),
		"start_time":         record.GetDateTime("start_time").Time(),
		"end_time":           record.GetDateTime("end_time").Time(),
		"status":             record.GetString("status"),
		"recurring":          record.GetBool("recurring"),
		"recurrence_pattern": record.GetString("recurrence_pattern"),
		"suppress_alerts":    record.GetBool("suppress_alerts"),
		"created":            record.GetDateTime("created").Time(),
		"updated":            record.GetDateTime("updated").Time(),
	}
}

// IsInMaintenanceWindow checks if a monitor or domain is currently in a maintenance window
func (h *APIHandler) IsInMaintenanceWindow(monitorID, domainID string) bool {
	now := time.Now()

	exp := dbx.NewExp("status = {:status} AND start_time <= {:now} AND end_time >= {:now}",
		dbx.Params{"status": "scheduled", "now": now})

	if monitorID != "" {
		exp = dbx.NewExp("status = {:status} AND start_time <= {:now} AND end_time >= {:now} AND (monitor = {:monitor} OR monitor = '')",
			dbx.Params{"status": "scheduled", "now": now, "monitor": monitorID})
	}
	if domainID != "" {
		exp = dbx.NewExp("status = {:status} AND start_time <= {:now} AND end_time >= {:now} AND (domain = {:domain} OR domain = '')",
			dbx.Params{"status": "scheduled", "now": now, "domain": domainID})
	}

	records, err := h.app.FindAllRecords("maintenance_windows", exp)
	if err != nil {
		return false
	}

	for _, record := range records {
		if record.GetBool("suppress_alerts") {
			return true
		}
	}

	return false
}
