// Package handlers provides HTTP handlers for the rdev API.
package handlers

import (
	"context"
	"errors"
	"fmt"
	"net/http"

	"github.com/go-chi/chi/v5"
	"github.com/orchard9/rdev/internal/auth"
	"github.com/orchard9/rdev/internal/domain"
	"github.com/orchard9/rdev/internal/port"
	"github.com/orchard9/rdev/internal/validate"
	"github.com/orchard9/rdev/pkg/api"
)

// CredentialsHandler handles credential management endpoints.
// These endpoints require superadmin authentication.
type CredentialsHandler struct {
	store port.CredentialStore
}

// NewCredentialsHandler creates a new credentials handler.
func NewCredentialsHandler(store port.CredentialStore) *CredentialsHandler {
	return &CredentialsHandler{store: store}
}

// updatedBy extracts the authenticated identity from the request context.
// Returns the API key name for regular keys, or "superadmin" for admin key auth
// (which has ID "admin") to preserve consistency with existing database records.
func updatedBy(ctx context.Context) string {
	if key := auth.GetAPIKey(ctx); key != nil && key.ID != "admin" {
		return key.Name
	}
	return "superadmin"
}

// Mount registers the credentials routes.
// All routes require admin authentication for security.
func (h *CredentialsHandler) Mount(r api.Router) {
	r.Route("/credentials", func(r chi.Router) {
		// All credential operations require admin scope
		r.With(auth.RequireScope(auth.ScopeAdmin)).Get("/", h.List)
		r.With(auth.RequireScope(auth.ScopeAdmin)).Post("/", h.Set)
		r.With(auth.RequireScope(auth.ScopeAdmin)).Post("/batch", h.SetBatch)
		r.With(auth.RequireScope(auth.ScopeAdmin)).Get("/{key}", h.Get)
		r.With(auth.RequireScope(auth.ScopeAdmin)).Delete("/{key}", h.Delete)
	})
}

// SetCredentialRequest is the request body for POST /credentials.
type SetCredentialRequest struct {
	Key         string `json:"key"`
	Value       string `json:"value"`
	Description string `json:"description,omitempty"`
	Category    string `json:"category,omitempty"`
}

// SetBatchRequest is the request body for POST /credentials/batch.
type SetBatchRequest struct {
	Credentials []SetCredentialRequest `json:"credentials"`
}

// CredentialResponse is the response for credential endpoints.
type CredentialResponse struct {
	Key         string `json:"key"`
	Value       string `json:"value,omitempty"` // Only included for Get, masked for List
	Description string `json:"description,omitempty"`
	Category    string `json:"category,omitempty"`
	CreatedAt   string `json:"created_at,omitempty"`
	UpdatedAt   string `json:"updated_at,omitempty"`
	UpdatedBy   string `json:"updated_by,omitempty"`
}

// List returns all credentials with values masked.
// GET /credentials
func (h *CredentialsHandler) List(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()

	// Check for category filter
	category := r.URL.Query().Get("category")

	var creds []domain.Credential
	var err error
	if category != "" {
		creds, err = h.store.ListByCategory(ctx, category)
	} else {
		creds, err = h.store.List(ctx)
	}
	if err != nil {
		api.WriteInternalError(w, r, "failed to list credentials")
		return
	}

	response := make([]CredentialResponse, len(creds))
	for i, c := range creds {
		response[i] = CredentialResponse{
			Key:         c.Key,
			Value:       c.Value, // Already masked by store
			Description: c.Description,
			Category:    c.Category,
			CreatedAt:   c.CreatedAt.Format("2006-01-02T15:04:05Z"),
			UpdatedAt:   c.UpdatedAt.Format("2006-01-02T15:04:05Z"),
			UpdatedBy:   c.UpdatedBy,
		}
	}

	api.WriteSuccess(w, r, response)
}

// Get retrieves a single credential by key.
// GET /credentials/{key}
func (h *CredentialsHandler) Get(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	key := chi.URLParam(r, "key")

	if key == "" {
		api.WriteBadRequest(w, r, "key is required")
		return
	}

	value, err := h.store.Get(ctx, key)
	if err != nil {
		api.WriteInternalError(w, r, "failed to get credential")
		return
	}
	if value == "" {
		api.WriteNotFound(w, r, "credential not found")
		return
	}

	api.WriteSuccess(w, r, CredentialResponse{
		Key:   key,
		Value: value,
	})
}

// Set creates or updates a single credential.
// POST /credentials
func (h *CredentialsHandler) Set(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()

	var req SetCredentialRequest
	if err := api.DecodeJSON(r, &req); err != nil {
		api.WriteBadRequest(w, r, "invalid request body")
		return
	}

	v := validate.New()
	v.Required(req.Key, "key")
	v.Required(req.Value, "value")
	if err := v.Error(); err != nil {
		api.WriteBadRequest(w, r, err.Error())
		return
	}

	cred := domain.Credential{
		Key:         req.Key,
		Value:       req.Value,
		Description: req.Description,
		Category:    req.Category,
		UpdatedBy:   updatedBy(ctx),
	}

	if err := h.store.Set(ctx, cred); err != nil {
		api.WriteInternalError(w, r, "failed to set credential")
		return
	}

	api.WriteCreated(w, r, map[string]string{
		"status": "stored",
		"key":    req.Key,
	})
}

// SetBatch creates or updates multiple credentials.
// POST /credentials/batch
func (h *CredentialsHandler) SetBatch(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()

	var req SetBatchRequest
	if err := api.DecodeJSON(r, &req); err != nil {
		api.WriteBadRequest(w, r, "invalid request body")
		return
	}

	if len(req.Credentials) == 0 {
		api.WriteBadRequest(w, r, "credentials array is required")
		return
	}

	creds := make([]domain.Credential, len(req.Credentials))
	for i, c := range req.Credentials {
		v := validate.New()
		v.Required(c.Key, fmt.Sprintf("credentials[%d].key", i))
		v.Required(c.Value, fmt.Sprintf("credentials[%d].value", i))
		if err := v.Error(); err != nil {
			api.WriteBadRequest(w, r, err.Error())
			return
		}
		creds[i] = domain.Credential{
			Key:         c.Key,
			Value:       c.Value,
			Description: c.Description,
			Category:    c.Category,
			UpdatedBy:   updatedBy(ctx),
		}
	}

	if err := h.store.SetMultiple(ctx, creds); err != nil {
		api.WriteInternalError(w, r, "failed to set credentials")
		return
	}

	keys := make([]string, len(creds))
	for i, c := range creds {
		keys[i] = c.Key
	}

	api.WriteCreated(w, r, map[string]any{
		"status": "stored",
		"count":  len(creds),
		"keys":   keys,
	})
}

// Delete removes a credential by key.
// DELETE /credentials/{key}
func (h *CredentialsHandler) Delete(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	key := chi.URLParam(r, "key")

	if key == "" {
		api.WriteBadRequest(w, r, "key is required")
		return
	}

	if err := h.store.Delete(ctx, key); err != nil {
		if errors.Is(err, domain.ErrCredentialNotFound) {
			api.WriteNotFound(w, r, "credential not found")
			return
		}
		api.WriteInternalError(w, r, "failed to delete credential")
		return
	}

	api.WriteSuccess(w, r, map[string]string{
		"status": "deleted",
		"key":    key,
	})
}
