package server

import (
	"context"
	"fmt"
	"net/http"
	"time"

	"github.com/titanappdev/rofs/apps/world/internal/auth"
	"github.com/titanappdev/rofs/apps/world/internal/config"
	"github.com/titanappdev/rofs/apps/world/internal/logging"
	redispkg "github.com/titanappdev/rofs/apps/world/internal/redis"
	"github.com/titanappdev/rofs/apps/world/internal/ws"
	"github.com/titanappdev/rofs/apps/world/internal/zone"
)

type Server struct {
	cfg        *config.Config
	log        *logging.Logger
	http       *http.Server
	zone       *zone.Zone
	redis      *redispkg.Client
	zoneCtx    context.Context
	zoneCancel context.CancelFunc
}

func New(ctx context.Context, cfg *config.Config, log *logging.Logger) (*Server, error) {
	rdb, err := redispkg.New(cfg)
	if err != nil {
		return nil, fmt.Errorf("redis required for ws tickets: %w", err)
	}

	zoneCtx, zoneCancel := context.WithCancel(ctx)
	z := zone.New("hammerfall_world", cfg.TickMS, 50, 50, log)
	z.Start(zoneCtx)

	ticketValidator := auth.NewValidator(rdb)
	gateway := ws.NewGateway(log, z, ticketValidator)

	mux := http.NewServeMux()
	mux.HandleFunc("/health", healthHandler)
	mux.HandleFunc("/ready", readyHandler)
	mux.Handle("/ws", gateway)

	addr := fmt.Sprintf("%s:%d", cfg.Host, cfg.Port)
	srv := &http.Server{
		Addr:              addr,
		Handler:           mux,
		ReadHeaderTimeout: 5 * time.Second,
	}

	return &Server{
		cfg: cfg, log: log, http: srv, zone: z, redis: rdb,
		zoneCtx: zoneCtx, zoneCancel: zoneCancel,
	}, nil
}

func (s *Server) Run() error {
	s.log.Info("listening", map[string]any{"addr": s.http.Addr})
	if err := s.http.ListenAndServe(); err != nil && err != http.ErrServerClosed {
		return fmt.Errorf("listen: %w", err)
	}
	return nil
}

func (s *Server) Shutdown(ctx context.Context) error {
	s.zoneCancel()
	s.zone.Stop()
	if s.redis != nil {
		_ = s.redis.Close()
	}
	return s.http.Shutdown(ctx)
}

func healthHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	_, _ = w.Write([]byte(`{"status":"ok","service":"world"}`))
}

func readyHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	_, _ = w.Write([]byte(`{"ready":true}`))
}
