// Package api provides HTTP service infrastructure for rdev.
package api

import (
	"context"
	"fmt"
	"log/slog"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

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

// Router is an alias for chi.Router, exposing it for handler mounting.
type Router = chi.Router

// App is the main application struct that provides HTTP infrastructure.
// It manages routing, logging, and graceful shutdown.
type App struct {
	name   string
	port   int
	logger *slog.Logger
	router chi.Router
	server *http.Server

	onShutdown []func(context.Context) error
}

// Option configures the App.
type Option func(*App)

// WithPort sets the server port.
func WithPort(port int) Option {
	return func(a *App) {
		a.port = port
	}
}

// WithLogger sets a custom logger.
func WithLogger(logger *slog.Logger) Option {
	return func(a *App) {
		a.logger = logger
	}
}

// New creates a new App instance.
// Note: Call Use() to add middleware before mounting routes.
func New(name string, opts ...Option) *App {
	app := &App{
		name:       name,
		port:       8080,
		onShutdown: make([]func(context.Context) error, 0),
	}

	for _, opt := range opts {
		opt(app)
	}

	if app.logger == nil {
		app.logger = slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
			Level: slog.LevelInfo,
		}))
	}

	app.router = chi.NewRouter()
	app.setupMiddleware()
	// Health endpoints are set up in Run() to allow middleware to be added first

	return app
}

// setupMiddleware configures the standard middleware stack.
func (a *App) setupMiddleware() {
	a.router.Use(middleware.RequestID)
	a.router.Use(middleware.RealIP)
	a.router.Use(middleware.Logger)
	a.router.Use(middleware.Recoverer)
	a.router.Use(middleware.Timeout(60 * time.Second))
}

// setupHealthEndpoints registers /health and /ready endpoints.
func (a *App) setupHealthEndpoints() {
	a.router.Get("/health", func(w http.ResponseWriter, r *http.Request) {
		WriteSuccess(w, r, map[string]string{
			"status":  "ok",
			"service": a.name,
		})
	})

	a.router.Get("/ready", func(w http.ResponseWriter, r *http.Request) {
		WriteSuccess(w, r, map[string]string{
			"status":  "ready",
			"service": a.name,
		})
	})
}

// Logger returns the application logger.
func (a *App) Logger() *slog.Logger {
	return a.logger
}

// Router returns the underlying chi router.
func (a *App) Router() chi.Router {
	return a.router
}

// Use appends middleware to the router middleware stack.
func (a *App) Use(middlewares ...func(http.Handler) http.Handler) {
	a.router.Use(middlewares...)
}

// GET registers a handler for GET requests.
func (a *App) GET(pattern string, handler http.HandlerFunc) {
	a.router.Get(pattern, handler)
}

// POST registers a handler for POST requests.
func (a *App) POST(pattern string, handler http.HandlerFunc) {
	a.router.Post(pattern, handler)
}

// PUT registers a handler for PUT requests.
func (a *App) PUT(pattern string, handler http.HandlerFunc) {
	a.router.Put(pattern, handler)
}

// PATCH registers a handler for PATCH requests.
func (a *App) PATCH(pattern string, handler http.HandlerFunc) {
	a.router.Patch(pattern, handler)
}

// DELETE registers a handler for DELETE requests.
func (a *App) DELETE(pattern string, handler http.HandlerFunc) {
	a.router.Delete(pattern, handler)
}

// Route creates a new sub-router with the given pattern prefix.
func (a *App) Route(pattern string, fn func(r chi.Router)) {
	a.router.Route(pattern, fn)
}

// Mount attaches a sub-router or http.Handler at the given pattern.
func (a *App) Mount(pattern string, handler http.Handler) {
	a.router.Mount(pattern, handler)
}

// OnShutdown registers a function to be called during graceful shutdown.
func (a *App) OnShutdown(fn func(context.Context) error) {
	a.onShutdown = append(a.onShutdown, fn)
}

// Run starts the HTTP server and blocks until shutdown.
func (a *App) Run() {
	// Set up health endpoints after all middleware has been added
	a.setupHealthEndpoints()

	addr := fmt.Sprintf(":%d", a.port)

	a.server = &http.Server{
		Addr:         addr,
		Handler:      a.router,
		ReadTimeout:  30 * time.Second,
		WriteTimeout: 30 * time.Second,
		IdleTimeout:  120 * time.Second,
	}

	errChan := make(chan error, 1)
	go func() {
		a.logger.Info("starting server",
			"service", a.name,
			"address", addr,
		)
		if err := a.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			errChan <- err
		}
	}()

	quit := make(chan os.Signal, 1)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)

	select {
	case err := <-errChan:
		a.logger.Error("server error", "error", err)
		os.Exit(1)
	case sig := <-quit:
		a.logger.Info("received shutdown signal", "signal", sig.String())
	}

	a.shutdown()
}

// shutdown performs graceful shutdown of the application.
func (a *App) shutdown() {
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	a.logger.Info("shutting down server")

	if err := a.server.Shutdown(ctx); err != nil {
		a.logger.Error("server shutdown error", "error", err)
	}

	for _, fn := range a.onShutdown {
		if err := fn(ctx); err != nil {
			a.logger.Error("shutdown hook error", "error", err)
		}
	}

	a.logger.Info("server stopped", "service", a.name)
}

// ServeHTTP implements http.Handler, allowing App to be used in tests.
func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	a.router.ServeHTTP(w, r)
}

// ListenAddr returns the address the server is configured to listen on.
func (a *App) ListenAddr() string {
	return fmt.Sprintf(":%d", a.port)
}
