// Copyright (c) 2021 - 2025, Ludvig Lundgren and the autobrr contributors.
// SPDX-License-Identifier: GPL-2.0-or-later

package http

import (
	"fmt"
	"net"
	"net/http"
	"time"

	"github.com/autobrr/autobrr/internal/config"
	"github.com/autobrr/autobrr/internal/logger"

	"github.com/go-chi/chi/v5"
	"github.com/go-chi/chi/v5/middleware"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/rs/zerolog"
)

type metricsManager interface {
	GetRegistry() *prometheus.Registry
}

type MetricsServer struct {
	log zerolog.Logger

	config *config.AppConfig

	version string
	commit  string
	date    string

	metricsManager metricsManager
}

func NewMetricsServer(log logger.Logger, config *config.AppConfig, version string, commit string, date string, metricsManager metricsManager) MetricsServer {
	return MetricsServer{
		log:     log.With().Str("module", "http").Logger(),
		config:  config,
		version: version,
		commit:  commit,
		date:    date,

		metricsManager: metricsManager,
	}
}

func (s MetricsServer) Open() error {
	addr := fmt.Sprintf("%v:%v", s.config.Config.MetricsHost, s.config.Config.MetricsPort)

	var err error
	for _, proto := range []string{"tcp", "tcp4", "tcp6"} {
		if err = s.tryToServe(addr, proto); err == nil {
			break
		}

		s.log.Error().Err(err).Msgf("Failed to start %s server. Attempted to listen on %s", proto, addr)
	}

	return err
}

func (s MetricsServer) tryToServe(addr, protocol string) error {
	listener, err := net.Listen(protocol, addr)
	if err != nil {
		return err
	}

	s.log.Info().Msgf("Starting Metrics %s server. Listening on %s", protocol, listener.Addr().String())

	server := http.Server{
		Handler:           s.Handler(),
		ReadHeaderTimeout: time.Second * 15,
	}

	return server.Serve(listener)
}

func (s MetricsServer) Handler() http.Handler {
	r := chi.NewRouter()

	r.Use(middleware.RequestID)
	r.Use(middleware.RealIP)
	r.Use(middleware.Recoverer)
	r.Use(LoggerMiddleware(&s.log))

	if s.config.Config.MetricsBasicAuthUsers != "" {
		r.Use(BasicAuth("metrics", s.config.Config.MetricsBasicAuthUsers))
	}

	r.Get("/metrics", promhttp.HandlerFor(s.metricsManager.GetRegistry(), promhttp.HandlerOpts{}).ServeHTTP)

	return r
}
