package api

import (
	"errors"
	"log/slog"
	"net/http"

	"github.com/go-chi/chi/v5"

	"github.com/alexei/docker-watcher/internal/events"
	"github.com/alexei/docker-watcher/internal/store"
)

// stageRequest is the expected JSON body for creating/updating a stage.
type stageRequest struct {
	Name            string `json:"name"`
	TagPattern      string `json:"tag_pattern"`
	AutoDeploy      *bool  `json:"auto_deploy"`
	MaxInstances    *int   `json:"max_instances"`
	Confirm         *bool  `json:"confirm"`
	PromoteFrom     string `json:"promote_from"`
	Subdomain       string `json:"subdomain"`
	NotificationURL string `json:"notification_url"`
}

// createStage handles POST /api/projects/{id}/stages.
func (s *Server) createStage(w http.ResponseWriter, r *http.Request) {
	projectID := chi.URLParam(r, "id")

	// Verify project exists.
	if _, err := s.store.GetProjectByID(projectID); err != nil {
		if errors.Is(err, store.ErrNotFound) {
			respondNotFound(w, "project")
			return
		}
		slog.Error("failed to get project", "error", err)
			respondError(w, http.StatusInternalServerError, "internal server error")
		return
	}

	var req stageRequest
	if !decodeJSON(w, r, &req) {
		return
	}

	if req.Name == "" {
		respondError(w, http.StatusBadRequest, "name is required")
		return
	}
	if req.TagPattern == "" {
		req.TagPattern = "*"
	}

	autoDeploy := false
	if req.AutoDeploy != nil {
		autoDeploy = *req.AutoDeploy
	}
	maxInstances := 1
	if req.MaxInstances != nil {
		maxInstances = *req.MaxInstances
	}
	confirm := false
	if req.Confirm != nil {
		confirm = *req.Confirm
	}

	stage, err := s.store.CreateStage(store.Stage{
		ProjectID:       projectID,
		Name:            req.Name,
		TagPattern:      req.TagPattern,
		AutoDeploy:      autoDeploy,
		MaxInstances:    maxInstances,
		Confirm:         confirm,
		PromoteFrom:     req.PromoteFrom,
		Subdomain:       req.Subdomain,
		NotificationURL: req.NotificationURL,
	})
	if err != nil {
		slog.Error("failed to create stage", "error", err)
		respondError(w, http.StatusInternalServerError, "internal server error")
		return
	}
	s.eventBus.Publish(events.Event{
		Type: events.EventLog,
		Payload: events.EventLogPayload{
			Source:   "admin",
			Severity: "info",
			Message:  "stage created: " + stage.Name,
		},
	})

	respondJSON(w, http.StatusCreated, stage)
}

// updateStage handles PUT /api/projects/{id}/stages/{stage}.
func (s *Server) updateStage(w http.ResponseWriter, r *http.Request) {
	stageID := chi.URLParam(r, "stage")

	existing, err := s.store.GetStageByID(stageID)
	if err != nil {
		if errors.Is(err, store.ErrNotFound) {
			respondNotFound(w, "stage")
			return
		}
		slog.Error("failed to get stage", "error", err)
			respondError(w, http.StatusInternalServerError, "internal server error")
		return
	}

	var req stageRequest
	if !decodeJSON(w, r, &req) {
		return
	}

	updated := existing
	if req.Name != "" {
		updated.Name = req.Name
	}
	if req.TagPattern != "" {
		updated.TagPattern = req.TagPattern
	}
	if req.AutoDeploy != nil {
		updated.AutoDeploy = *req.AutoDeploy
	}
	if req.MaxInstances != nil {
		updated.MaxInstances = *req.MaxInstances
	}
	if req.Confirm != nil {
		updated.Confirm = *req.Confirm
	}
	updated.PromoteFrom = req.PromoteFrom
	updated.Subdomain = req.Subdomain
	updated.NotificationURL = req.NotificationURL

	if err := s.store.UpdateStage(updated); err != nil {
		slog.Error("failed to update stage", "error", err)
		respondError(w, http.StatusInternalServerError, "internal server error")
		return
	}
	s.eventBus.Publish(events.Event{
		Type: events.EventLog,
		Payload: events.EventLogPayload{
			Source:   "admin",
			Severity: "info",
			Message:  "stage updated: " + updated.Name,
		},
	})

	respondJSON(w, http.StatusOK, updated)
}

// deleteStage handles DELETE /api/projects/{id}/stages/{stage}.
func (s *Server) deleteStage(w http.ResponseWriter, r *http.Request) {
	stageID := chi.URLParam(r, "stage")
	if err := s.store.DeleteStage(stageID); err != nil {
		if errors.Is(err, store.ErrNotFound) {
			respondNotFound(w, "stage")
			return
		}
		slog.Error("failed to delete stage", "error", err)
			respondError(w, http.StatusInternalServerError, "internal server error")
		return
	}
	s.eventBus.Publish(events.Event{
		Type: events.EventLog,
		Payload: events.EventLogPayload{
			Source:   "admin",
			Severity: "info",
			Message:  "stage deleted: " + stageID,
		},
	})

	respondJSON(w, http.StatusOK, map[string]string{"deleted": stageID})
}
