// Package cmd is the AdGuard Home entry point.  It assembles the configuration
// file manager, sets up signal processing logic, and so on.
//
// TODO(a.garipov): Move to the upper-level internal/.
package cmd

import (
	"context"
	"io/fs"
	"os"
	"time"

	"github.com/AdguardTeam/AdGuardHome/internal/next/configmgr"
	"github.com/AdguardTeam/AdGuardHome/internal/version"
	"github.com/AdguardTeam/golibs/log"
)

// Main is the entry point of AdGuard Home.
func Main(embeddedFrontend fs.FS) {
	start := time.Now()

	cmdName := os.Args[0]
	opts, err := parseOptions(cmdName, os.Args[1:])
	exitCode, needExit := processOptions(opts, cmdName, err)
	if needExit {
		os.Exit(exitCode)
	}

	err = setLog(opts)
	check(err)

	log.Info("starting adguard home, version %s, pid %d", version.Version(), os.Getpid())

	if opts.workDir != "" {
		log.Info("changing working directory to %q", opts.workDir)
		err = os.Chdir(opts.workDir)
		check(err)
	}

	frontend, err := frontendFromOpts(opts, embeddedFrontend)
	check(err)

	confMgrConf := &configmgr.Config{
		Frontend: frontend,
		WebAddr:  opts.webAddr,
		Start:    start,
		FileName: opts.confFile,
	}

	confMgr, err := newConfigMgr(confMgrConf)
	check(err)

	web := confMgr.Web()
	err = web.Start()
	check(err)

	dns := confMgr.DNS()
	err = dns.Start()
	check(err)

	sigHdlr := newSignalHandler(
		confMgrConf,
		opts.pidFile,
		web,
		dns,
	)

	sigHdlr.handle()
}

// defaultTimeout is the timeout used for some operations where another timeout
// hasn't been defined yet.
const defaultTimeout = 5 * time.Second

// ctxWithDefaultTimeout is a helper function that returns a context with
// timeout set to defaultTimeout.
func ctxWithDefaultTimeout() (ctx context.Context, cancel context.CancelFunc) {
	return context.WithTimeout(context.Background(), defaultTimeout)
}

// newConfigMgr returns a new configuration manager using defaultTimeout as the
// context timeout.
func newConfigMgr(c *configmgr.Config) (m *configmgr.Manager, err error) {
	ctx, cancel := ctxWithDefaultTimeout()
	defer cancel()

	return configmgr.New(ctx, c)
}

// check is a simple error-checking helper.  It must only be used within Main.
func check(err error) {
	if err != nil {
		panic(err)
	}
}