package serverimp

import (
	"context"
	"errors"
	"net"
	"net/http"
	"strings"
	"time"

	"github.com/go-chi/chi/v5"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/tech/sendico/pkg/api/routers"
	"github.com/tech/sendico/pkg/api/routers/health"
	"go.uber.org/zap"
)

const readHeaderTimeout = 5 * time.Second

func (i *Imp) startMetrics(cfg *metricsConfig) {
	if i == nil {
		return
	}

	address := ""
	if cfg != nil {
		address = strings.TrimSpace(cfg.Address)
	}

	if address == "" {
		i.logger.Info("Metrics endpoint disabled")

		return
	}

	lc := net.ListenConfig{}

	listener, err := lc.Listen(context.Background(), "tcp", address)
	if err != nil {
		i.logger.Error("Failed to bind metrics listener", zap.String("address", address), zap.Error(err))

		return
	}

	router := chi.NewRouter()
	router.Handle("/metrics", promhttp.Handler())

	var healthRouter routers.Health

	hr, err := routers.NewHealthRouter(i.logger.Named("metrics"), router, "")
	if err != nil {
		i.logger.Warn("Failed to initialise health router", zap.Error(err))
	} else {
		hr.SetStatus(health.SSStarting)
		healthRouter = hr
	}

	i.metricsHealth = healthRouter
	i.metricsSrv = &http.Server{
		Addr:              address,
		Handler:           router,
		ReadHeaderTimeout: readHeaderTimeout,
	}

	go func() {
		i.logger.Info("Prometheus endpoint listening", zap.String("address", address))

		serveErr := i.metricsSrv.Serve(listener)
		if serveErr != nil && !errors.Is(serveErr, http.ErrServerClosed) {
			i.logger.Error("Prometheus endpoint stopped unexpectedly", zap.Error(serveErr))

			if healthRouter != nil {
				healthRouter.SetStatus(health.SSTerminating)
			}
		}
	}()
}

func (i *Imp) shutdownMetrics(ctx context.Context) {
	if i.metricsHealth != nil {
		i.metricsHealth.SetStatus(health.SSTerminating)
		i.metricsHealth.Finish()
		i.metricsHealth = nil
	}

	if i.metricsSrv == nil {
		return
	}

	err := i.metricsSrv.Shutdown(ctx)
	if err != nil && !errors.Is(err, http.ErrServerClosed) {
		i.logger.Warn("Failed to stop metrics server", zap.Error(err))
	} else {
		i.logger.Info("Metrics server stopped")
	}

	i.metricsSrv = nil
}

func (i *Imp) setMetricsStatus(status health.ServiceStatus) {
	if i == nil || i.metricsHealth == nil {
		return
	}

	i.metricsHealth.SetStatus(status)
}
