package service

import (
	"context"
	"encoding/json"
	"log"
	"net/http"
	"os"
	"strings"
	"time"
)

// Config describes one internal Go service behind the Elysia gateway.
type Config struct {
	Name         string   `json:"name"`
	Domain       string   `json:"domain"`
	DefaultPort  string   `json:"defaultPort"`
	Capabilities []string `json:"capabilities"`
}

type response struct {
	Status       string    `json:"status"`
	Service      string    `json:"service"`
	Domain       string    `json:"domain"`
	Capabilities []string  `json:"capabilities,omitempty"`
	Error        string    `json:"error,omitempty"`
	Time         time.Time `json:"time"`
}

// EnvPort reads PORT with a safe fallback for local service runs.
func EnvPort(defaultPort string) string {
	if port := strings.TrimSpace(os.Getenv("PORT")); port != "" {
		return port
	}
	if strings.TrimSpace(defaultPort) != "" {
		return defaultPort
	}
	return "8080"
}

// MustRun starts a service and terminates the process if startup fails.
func MustRun(cfg Config) {
	if err := Run(cfg); err != nil {
		log.Fatalf("%s stopped: %v", cfg.Name, err)
	}
}

// Run starts an HTTP server for an internal service.
func Run(cfg Config) error {
	if _, err := bootstrapDefaultStore(); err != nil {
		return err
	}
	port := EnvPort(cfg.DefaultPort)
	server := &http.Server{
		Addr:              ":" + port,
		Handler:           requestIDMiddleware(loggingMiddleware(NewHandler(cfg))),
		ReadHeaderTimeout: 5 * time.Second,
	}

	log.Printf("%s listening on :%s", cfg.Name, port)
	return server.ListenAndServe()
}

// NewHandler returns the standard internal service HTTP API.
func NewHandler(cfg Config) http.Handler {
	if strings.TrimSpace(os.Getenv("DATABASE_URL")) != "" {
		_, _ = bootstrapDefaultStore()
	}
	if cfg.Name == "" {
		cfg.Name = "Fable Service"
	}
	if cfg.Domain == "" {
		cfg.Domain = "unknown"
	}

	mux := http.NewServeMux()
	mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
		store := backendStore
		status := http.StatusOK
		payload := response{
			Status:       "ok",
			Service:      cfg.Name,
			Domain:       cfg.Domain,
			Capabilities: cfg.Capabilities,
			Time:         time.Now().UTC(),
		}
		ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)
		defer cancel()
		if err := store.Ping(ctx); err != nil {
			status = http.StatusServiceUnavailable
			payload.Status = "degraded"
			payload.Error = err.Error()
		}
		writeJSON(w, status, payload)
	})
	mux.HandleFunc("/ready", func(w http.ResponseWriter, r *http.Request) {
		store := backendStore
		status := http.StatusOK
		payload := response{
			Status:       "ready",
			Service:      cfg.Name,
			Domain:       cfg.Domain,
			Capabilities: cfg.Capabilities,
			Time:         time.Now().UTC(),
		}
		ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)
		defer cancel()
		if err := store.Ping(ctx); err != nil {
			status = http.StatusServiceUnavailable
			payload.Status = "not_ready"
			payload.Error = err.Error()
		}
		writeJSON(w, status, payload)
	})
	mux.HandleFunc("/metadata", func(w http.ResponseWriter, r *http.Request) {
		writeJSON(w, http.StatusOK, cfg)
	})
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if routeAPI(w, r, cfg) {
			return
		}
		if r.URL.Path != "/" {
			writeJSON(w, http.StatusNotFound, map[string]string{"error": "route not found"})
			return
		}
		writeJSON(w, http.StatusOK, response{
			Status:       "ok",
			Service:      cfg.Name,
			Domain:       cfg.Domain,
			Capabilities: cfg.Capabilities,
			Time:         time.Now().UTC(),
		})
	})

	return mux
}

func writeJSON(w http.ResponseWriter, status int, payload any) {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.WriteHeader(status)
	if err := json.NewEncoder(w).Encode(payload); err != nil {
		log.Printf("write response: %v", err)
	}
}

func requestIDMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		requestID := strings.TrimSpace(r.Header.Get("X-Request-Id"))
		if requestID == "" {
			requestID = time.Now().UTC().Format("20060102150405.000000000")
		}
		w.Header().Set("X-Request-Id", requestID)
		next.ServeHTTP(w, r)
	})
}

func loggingMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		started := time.Now()
		next.ServeHTTP(w, r)
		log.Printf("method=%s path=%s duration=%s", r.Method, r.URL.Path, time.Since(started))
	})
}
