package httpx

import (
	"context"
	"net/http"
	"strings"
	"time"

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

	"royal-pop-backend/internal/config"
	"royal-pop-backend/internal/database"
	"royal-pop-backend/internal/inventory"
	"royal-pop-backend/internal/mailer"
	"royal-pop-backend/internal/orders"
)

type RouterConfig struct {
	Config *config.Config
	DB     *database.DB
	Orders *orders.Store
	Stock  *inventory.Store
	Mailer *mailer.ResendMailer
}

func NewRouter(cfg RouterConfig) http.Handler {
	router := chi.NewRouter()

	router.Use(chimiddleware.RealIP)
	router.Use(chimiddleware.RequestID)
	router.Use(chimiddleware.Recoverer)
	router.Use(chimiddleware.Timeout(30 * time.Second))
	router.Use(corsMiddleware(cfg.Config.AllowedOrigins))

	router.Get("/healthz", func(w http.ResponseWriter, _ *http.Request) {
		databaseStatus := "disabled"
		if cfg.DB != nil {
			healthCtx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
			defer cancel()
			if err := cfg.DB.Health(healthCtx); err != nil {
				WriteJSON(w, http.StatusServiceUnavailable, map[string]any{
					"service":  cfg.Config.AppName,
					"status":   "degraded",
					"database": "unreachable",
				})
				return
			}
			databaseStatus = "ok"
		}

		WriteJSON(w, http.StatusOK, map[string]any{
			"service":  cfg.Config.AppName,
			"status":   "ok",
			"database": databaseStatus,
		})
	})

	router.Post("/webhooks/stripe", handleStripeWebhook(cfg.Config, cfg.Orders, cfg.Mailer))

	router.Route("/v1", func(api chi.Router) {
		api.Get("/inventory", handleListPublicInventory(cfg.Stock))
		api.Get("/storefront/pricing", handleGetStorefrontPricing(cfg.Config))
		api.Get("/", func(w http.ResponseWriter, _ *http.Request) {
			WriteJSON(w, http.StatusOK, map[string]any{
				"service": cfg.Config.AppName,
				"status":  "scaffolded",
				"version": "v1",
			})
		})

		api.Post("/checkout/payment-intent", handleCreatePaymentIntent(cfg.Config, cfg.Orders))
		api.Get("/checkout/payment-intent/{paymentIntentID}", handleGetPaymentIntentStatus(cfg.Config, cfg.Orders))
		api.Get("/client/session", handleGetClientSession(cfg.Config))
		api.Post("/client/login", handleClientLogin(cfg.Config))
		api.Post("/client/logout", handleClientLogout(cfg.Config))

		api.Group(func(client chi.Router) {
			client.Use(requireClientSession(cfg.Config))
			client.Get("/client/orders", handleListClientOrders(cfg.Orders))
			client.Get("/client/orders/{orderID}", handleGetClientOrder(cfg.Orders))
			client.Patch("/client/orders/{orderID}", handleUpdateClientOrder(cfg.Orders, cfg.Mailer))
			client.Get("/client/stock", handleListClientStock(cfg.Stock))
			client.Put("/client/stock", handleUpsertClientStock(cfg.Stock))
			client.Post("/client/reseed", handleClientReseed(cfg.DB))
		})
	})

	router.NotFound(func(w http.ResponseWriter, _ *http.Request) {
		WriteError(w, http.StatusNotFound, "not_found", "The requested endpoint does not exist.")
	})

	return router
}

func corsMiddleware(allowedOrigins []string) func(http.Handler) http.Handler {
	normalized := make([]string, 0, len(allowedOrigins))
	for _, origin := range allowedOrigins {
		trimmed := strings.TrimSpace(origin)
		if trimmed == "" {
			continue
		}
		normalized = append(normalized, trimmed)
	}

	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			origin := r.Header.Get("Origin")

			if origin != "" {
				for _, allowedOrigin := range normalized {
					if allowedOrigin == "*" || strings.EqualFold(allowedOrigin, origin) {
						w.Header().Set("Access-Control-Allow-Origin", origin)
						w.Header().Set("Vary", "Origin")
						w.Header().Set("Access-Control-Allow-Credentials", "true")
						break
					}
				}
			}

			w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
			w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, OPTIONS")

			if r.Method == http.MethodOptions {
				w.WriteHeader(http.StatusNoContent)
				return
			}

			next.ServeHTTP(w, r)
		})
	}
}
