package api

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

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

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

// stageEnvRequest is the expected JSON body for creating/updating a stage env override.
type stageEnvRequest struct {
	Key       string `json:"key"`
	Value     string `json:"value"`
	Encrypted *bool  `json:"encrypted"`
}

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

	// Verify stage exists.
	if _, err := s.store.GetStageByID(stageID); 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
	}

	envs, err := s.store.GetStageEnvByStageID(stageID)
	if err != nil {
		slog.Error("failed to list stage env", "error", err)
		respondError(w, http.StatusInternalServerError, "internal server error")
		return
	}

	// Mask encrypted values in the response.
	masked := make([]store.StageEnv, len(envs))
	for i, env := range envs {
		masked[i] = env
		if env.Encrypted {
			masked[i].Value = "••••••••"
		}
	}

	respondJSON(w, http.StatusOK, masked)
}

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

	// Verify stage exists.
	if _, err := s.store.GetStageByID(stageID); 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 stageEnvRequest
	if !decodeJSON(w, r, &req) {
		return
	}

	if req.Key == "" {
		respondError(w, http.StatusBadRequest, "key is required")
		return
	}

	encrypted := false
	if req.Encrypted != nil {
		encrypted = *req.Encrypted
	}

	value := req.Value
	if encrypted && value != "" {
		enc, err := crypto.Encrypt(s.encKey, value)
		if err != nil {
			slog.Error("failed to encrypt value", "error", err)
				respondError(w, http.StatusInternalServerError, "internal server error")
			return
		}
		value = enc
	}

	env, err := s.store.CreateStageEnv(store.StageEnv{
		StageID:   stageID,
		Key:       req.Key,
		Value:     value,
		Encrypted: encrypted,
	})
	if err != nil {
		slog.Error("failed to create stage env", "error", err)
		respondError(w, http.StatusInternalServerError, "internal server error")
		return
	}

	// Mask encrypted value in the response.
	if env.Encrypted {
		env.Value = "••••••••"
	}

	respondJSON(w, http.StatusCreated, env)
}

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

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

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

	updated := existing
	if req.Key != "" {
		updated.Key = req.Key
	}
	if req.Encrypted != nil {
		updated.Encrypted = *req.Encrypted
	}

	// Only update value if provided (allows updating key/encrypted without changing the value).
	if req.Value != "" {
		value := req.Value
		if updated.Encrypted {
			enc, err := crypto.Encrypt(s.encKey, value)
			if err != nil {
				slog.Error("failed to encrypt value", "error", err)
				respondError(w, http.StatusInternalServerError, "internal server error")
				return
			}
			value = enc
		}
		updated.Value = value
	}

	if err := s.store.UpdateStageEnv(updated); err != nil {
		slog.Error("failed to update stage env", "error", err)
		respondError(w, http.StatusInternalServerError, "internal server error")
		return
	}

	// Mask encrypted value in the response.
	if updated.Encrypted {
		updated.Value = "••••••••"
	}

	respondJSON(w, http.StatusOK, updated)
}

// deleteStageEnv handles DELETE /api/projects/{id}/stages/{stage}/env/{envId}.
func (s *Server) deleteStageEnv(w http.ResponseWriter, r *http.Request) {
	envID := chi.URLParam(r, "envId")
	if err := s.store.DeleteStageEnv(envID); err != nil {
		if errors.Is(err, store.ErrNotFound) {
			respondNotFound(w, "stage env")
			return
		}
		slog.Error("failed to delete stage env", "error", err)
			respondError(w, http.StatusInternalServerError, "internal server error")
		return
	}
	respondJSON(w, http.StatusOK, map[string]string{"deleted": envID})
}
