// Command oohelperd implements the Web Connectivity test helper.
package main

import (
	"context"
	"flag"
	"net"
	"net/http"
	"sync"
	"time"

	"github.com/apex/log"
	"github.com/ooni/probe-cli/v3/internal/atomicx"
	"github.com/ooni/probe-cli/v3/internal/model"
	"github.com/ooni/probe-cli/v3/internal/netxlite"
	"github.com/ooni/probe-cli/v3/internal/runtimex"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

const maxAcceptableBody = 1 << 24

var (
	endpoint  = flag.String("endpoint", "127.0.0.1:8080", "API endpoint")
	srvAddr   = make(chan string, 1) // with buffer
	srvCancel context.CancelFunc
	srvCtx    context.Context
	srvWg     = new(sync.WaitGroup)
)

func init() {
	srvCtx, srvCancel = context.WithCancel(context.Background())
}

func newResolver(logger model.Logger) model.Resolver {
	// Implementation note: pin to a specific resolver so we don't depend upon the
	// default resolver configured by the box. Also, use an encrypted transport thus
	// we're less vulnerable to any policy implemented by the box's provider.
	resolver := netxlite.NewParallelDNSOverHTTPSResolver(logger, "https://dns.google/dns-query")
	return resolver
}

func shutdown(srv *http.Server) {
	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancel()
	srv.Shutdown(ctx)
}

func main() {
	logmap := map[bool]log.Level{
		true:  log.DebugLevel,
		false: log.InfoLevel,
	}
	prometheus := flag.String("prometheus", "127.0.0.1:9091", "Prometheus endpoint")
	debug := flag.Bool("debug", false, "Toggle debug mode")
	flag.Parse()
	log.SetLevel(logmap[*debug])
	defer srvCancel()
	mux := http.NewServeMux()
	mux.Handle("/", &handler{
		BaseLogger:        log.Log,
		Indexer:           &atomicx.Int64{},
		MaxAcceptableBody: maxAcceptableBody,
		NewClient: func(logger model.Logger) model.HTTPClient {
			// If the DoH resolver we're using insists that a given domain maps to
			// bogons, make sure we're going to fail the HTTP measurement.
			//
			// The TCP measurements scheduler in ipinfo.go will also refuse to
			// schedule TCP measurements for bogons.
			//
			// While this seems theoretical, as of 2022-08-28, I see:
			//
			//     % host polito.it
			//     polito.it has address 192.168.59.6
			//     polito.it has address 192.168.40.1
			//     polito.it mail is handled by 10 mx.polito.it.
			//
			// So, it's better to consider this as a possible corner case.
			reso := netxlite.MaybeWrapWithBogonResolver(
				true, // enabled
				newResolver(logger),
			)
			return netxlite.NewHTTPClientWithResolver(logger, reso)
		},
		NewDialer: func(logger model.Logger) model.Dialer {
			return netxlite.NewDialerWithResolver(logger, newResolver(logger))
		},
		NewResolver: newResolver,
		NewTLSHandshaker: func(logger model.Logger) model.TLSHandshaker {
			return netxlite.NewTLSHandshakerStdlib(logger)
		},
	})
	srv := &http.Server{Addr: *endpoint, Handler: mux}
	listener, err := net.Listen("tcp", *endpoint)
	runtimex.PanicOnError(err, "net.Listen failed")
	srvAddr <- listener.Addr().String()
	srvWg.Add(1)
	go srv.Serve(listener)
	promMux := http.NewServeMux()
	promMux.Handle("/metrics", promhttp.Handler())
	promSrv := &http.Server{Addr: *prometheus, Handler: promMux}
	go promSrv.ListenAndServe()
	<-srvCtx.Done()
	shutdown(srv)
	shutdown(promSrv)
	listener.Close()
	srvWg.Done()
}
