package api

import (
	"net/http"

	"github.com/go-chi/chi/v5"
	"github.com/go-chi/chi/v5/middleware"
	"github.com/eduard256/Strix/internal/api/handlers"
	"github.com/eduard256/Strix/internal/camera/database"
	"github.com/eduard256/Strix/internal/camera/discovery"
	"github.com/eduard256/Strix/internal/camera/stream"
	"github.com/eduard256/Strix/internal/config"
	"github.com/eduard256/Strix/pkg/sse"
)

// Server represents the API server
type Server struct {
	router       chi.Router
	config       *config.Config
	loader       *database.Loader
	searchEngine *database.SearchEngine
	scanner      *discovery.Scanner
	probeService *discovery.ProbeService
	sseServer    *sse.Server
	logger       interface{ Debug(string, ...any); Error(string, error, ...any); Info(string, ...any) }
}

// NewServer creates a new API server
func NewServer(
	cfg *config.Config,
	logger interface{ Debug(string, ...any); Error(string, error, ...any); Info(string, ...any) },
) (*Server, error) {
	// Initialize database loader
	loader := database.NewLoader(
		cfg.Database.BrandsPath,
		cfg.Database.PatternsPath,
		cfg.Database.ParametersPath,
		logger,
	)

	// Load query parameters for URL builder
	queryParams, err := loader.LoadQueryParameters()
	if err != nil {
		return nil, err
	}

	// Initialize search engine
	searchEngine := database.NewSearchEngine(loader, logger)

	// Initialize stream components
	builder := stream.NewBuilder(queryParams, logger)
	tester := stream.NewTester(cfg.Scanner.FFProbeTimeout, logger)

	// Initialize ONVIF discovery
	onvif := discovery.NewONVIFDiscovery(logger)

	// Initialize scanner
	scannerConfig := discovery.ScannerConfig{
		WorkerPoolSize:   cfg.Scanner.WorkerPoolSize,
		DefaultTimeout:   cfg.Scanner.DefaultTimeout,
		MaxStreams:       cfg.Scanner.MaxStreams,
		ModelSearchLimit: cfg.Scanner.ModelSearchLimit,
		FFProbeTimeout:   cfg.Scanner.FFProbeTimeout,
	}

	scanner := discovery.NewScanner(
		loader,
		searchEngine,
		builder,
		tester,
		onvif,
		scannerConfig,
		logger,
	)

	// Initialize SSE server
	sseServer := sse.NewServer(logger)

	// Initialize OUI database for vendor identification
	ouiDB := discovery.NewOUIDatabase()
	if err := ouiDB.LoadFromFile(cfg.Database.OUIPath); err != nil {
		logger.Error("failed to load OUI database, vendor lookup will be unavailable", err)
	} else {
		logger.Info("OUI database loaded", "entries", ouiDB.Size())
	}

	// Initialize ProbeService with all probers
	probers := []discovery.Prober{
		&discovery.DNSProber{},
		discovery.NewARPProber(ouiDB),
		&discovery.MDNSProber{},
		&discovery.HTTPProber{},
	}
	probeService := discovery.NewProbeService(probers, logger)

	// Create server
	server := &Server{
		router:       chi.NewRouter(),
		config:       cfg,
		loader:       loader,
		searchEngine: searchEngine,
		scanner:      scanner,
		probeService: probeService,
		sseServer:    sseServer,
		logger:       logger,
	}

	// Setup routes
	server.setupRoutes()

	return server, nil
}

// setupRoutes configures all routes and middleware
func (s *Server) setupRoutes() {
	// Global middleware
	s.router.Use(middleware.RequestID)
	s.router.Use(middleware.RealIP)
	s.router.Use(middleware.Logger)
	s.router.Use(middleware.Recoverer)
	// Note: No global timeout middleware - endpoints use context-based timeouts from request parameters

	// CORS middleware
	s.router.Use(func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.Header().Set("Access-Control-Allow-Origin", "*")
			w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
			w.Header().Set("Access-Control-Allow-Headers", "Accept, Authorization, Content-Type, X-Request-ID")
			w.Header().Set("Access-Control-Max-Age", "3600")

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

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

	// API routes (mounted at /api/v1 in main.go)
	// Health check
	s.router.Get("/health", handlers.NewHealthHandler(s.config.Version, s.logger).ServeHTTP)

	// Camera search
	s.router.Post("/cameras/search", handlers.NewSearchHandler(s.searchEngine, s.logger).ServeHTTP)

	// Stream discovery (SSE)
	s.router.Post("/streams/discover", handlers.NewDiscoverHandler(s.scanner, s.sseServer, s.logger).ServeHTTP)

	// Device probe (ping + DNS + ARP/OUI + mDNS)
	s.router.Get("/probe", handlers.NewProbeHandler(s.probeService, s.logger).ServeHTTP)
}

// ServeHTTP implements http.Handler
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	s.router.ServeHTTP(w, r)
}

// GetRouter returns the chi router
func (s *Server) GetRouter() chi.Router {
	return s.router
}