package api

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

	"drivervault/apiserver/internal/plugins"
)

// pluginsReady guards every plugin endpoint. Until the settings have been read
// from PocketBase the server knows of no plugins — reporting that as an empty or
// all-disabled list would be a lie the panel could then save back over the real
// settings, so the endpoints answer 503 instead.
func (s *Server) pluginsReady(w http.ResponseWriter) bool {
	if s.plugins.Ready() {
		return true
	}
	writeError(w, http.StatusServiceUnavailable,
		"plugin settings are not loaded yet — the database is unreachable; retrying")
	return false
}

// GET /api/admin/plugins — every known plugin (registry ∪ persisted), secrets masked.
func (s *Server) handleListPlugins(w http.ResponseWriter, r *http.Request) {
	if !s.pluginsReady(w) {
		return
	}
	writeJSON(w, http.StatusOK, map[string]any{"plugins": s.plugins.List()})
}

// GET /api/admin/plugins/{name} — one plugin's view.
func (s *Server) handleGetPlugin(w http.ResponseWriter, r *http.Request) {
	if !s.pluginsReady(w) {
		return
	}
	v, ok := s.plugins.Get(r.PathValue("name"))
	if !ok {
		writeError(w, http.StatusNotFound, "unknown plugin")
		return
	}
	writeJSON(w, http.StatusOK, map[string]any{"plugin": v})
}

// PUT /api/admin/plugins/{name} — enable/disable + merge config. Body:
// {enabled?, config?}. A secret left at the mask keeps its stored value.
func (s *Server) handleUpdatePlugin(w http.ResponseWriter, r *http.Request) {
	if !s.pluginsReady(w) {
		return
	}
	name := r.PathValue("name")
	current, ok := s.plugins.Get(name)
	if !ok {
		writeError(w, http.StatusNotFound, "unknown plugin")
		return
	}
	var body struct {
		Enabled *bool             `json:"enabled"`
		Config  map[string]string `json:"config"`
	}
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		writeError(w, http.StatusBadRequest, "invalid json")
		return
	}
	enabled := current.Enabled
	if body.Enabled != nil {
		enabled = *body.Enabled
	}

	v, err := s.plugins.Upsert(r.Context(), name, enabled, body.Config)
	switch {
	case err == nil:
		writeJSON(w, http.StatusOK, map[string]any{"plugin": v})
	case plugins.IsUnknown(err):
		writeError(w, http.StatusNotFound, "unknown plugin")
	case plugins.IsNotReady(err):
		writeError(w, http.StatusServiceUnavailable, err.Error())
	case plugins.IsPersist(err):
		// The change never reached the store and has been rolled back.
		// Reporting this as a 200-with-warning is what let a plugin look
		// enabled in the panel and come back disabled after a redeploy.
		writeError(w, http.StatusInternalServerError, err.Error())
	default:
		// A failed init (e.g. bad credentials) is reported but the state was saved.
		writeJSON(w, http.StatusOK, map[string]any{"plugin": v, "warning": err.Error()})
	}
}

// POST /api/admin/plugins — register an external (remote HTTP) plugin. Body:
// {name, baseURL, provider?}. This is the "add a plugin without a rebuild" path.
func (s *Server) handleRegisterPlugin(w http.ResponseWriter, r *http.Request) {
	if !s.pluginsReady(w) {
		return
	}
	var body struct {
		Name     string `json:"name"`
		BaseURL  string `json:"baseURL"`
		Provider string `json:"provider"`
	}
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		writeError(w, http.StatusBadRequest, "invalid json")
		return
	}
	body.Name = strings.TrimSpace(body.Name)
	if body.Name == "" || body.BaseURL == "" {
		writeError(w, http.StatusBadRequest, "name and baseURL are required")
		return
	}
	if err := s.plugins.RegisterExternal(r.Context(), body.Name, body.BaseURL, body.Provider); err != nil {
		if plugins.IsPersist(err) {
			writeError(w, http.StatusInternalServerError, err.Error())
			return
		}
		writeError(w, http.StatusConflict, err.Error())
		return
	}
	v, _ := s.plugins.Get(body.Name)
	writeJSON(w, http.StatusCreated, map[string]any{"plugin": v})
}

// DELETE /api/admin/plugins/{name} — remove an external plugin (builtins can
// only be disabled).
func (s *Server) handleDeletePlugin(w http.ResponseWriter, r *http.Request) {
	if !s.pluginsReady(w) {
		return
	}
	if err := s.plugins.Remove(r.Context(), r.PathValue("name")); err != nil {
		if plugins.IsPersist(err) {
			writeError(w, http.StatusInternalServerError, err.Error())
			return
		}
		writeError(w, http.StatusBadRequest, err.Error())
		return
	}
	writeJSON(w, http.StatusOK, map[string]any{"ok": true})
}

// POST /api/admin/plugins/{name}/health — run a health check now. Works on
// disabled plugins too, so a config can be verified before enabling it.
func (s *Server) handlePluginHealth(w http.ResponseWriter, r *http.Request) {
	if !s.pluginsReady(w) {
		return
	}
	h, err := s.plugins.HealthCheck(r.Context(), r.PathValue("name"))
	if err != nil {
		if plugins.IsUnknown(err) {
			writeError(w, http.StatusNotFound, "unknown plugin")
			return
		}
		writeJSON(w, http.StatusBadGateway, map[string]any{"error": err.Error()})
		return
	}
	writeJSON(w, http.StatusOK, map[string]any{"health": h})
}
