//go:generate go install github.com/valyala/quicktemplate/qtc@latest
//go:generate qtc -dir=web/template
//go:generate go install golang.org/x/text/cmd/gotext@latest
//go:generate gotext -srclang=en update -out=catalog_gen.go -lang=en,ru
package main

import (
	"context"
	"crypto/tls"
	"errors"
	"flag"
	"log"
	"net"
	"os"
	"os/signal"
	"path/filepath"
	"runtime"
	"runtime/pprof"
	"syscall"
	_ "time/tzdata"

	"github.com/caarlos0/env/v10"

	"source.toby3d.me/toby3d/home/internal/cmd/home"
	"source.toby3d.me/toby3d/home/internal/domain"
)

var (
	config = new(domain.Config)
	logger = log.New(os.Stdout, "home\t", log.Lmsgprefix|log.LstdFlags|log.LUTC)
)

var cpuProfilePath, memProfilePath string

func main() {
	flag.StringVar(&cpuProfilePath, "cpuprofile", "", "set path to saving CPU memory profile")
	flag.StringVar(&memProfilePath, "memprofile", "", "set path to saving pprof memory profile")
	flag.Parse()

	var err error
	if err = env.ParseWithOptions(config, env.Options{Prefix: "HOME_"}); err != nil {
		logger.Fatalln("cannot unmarshal configuration into domain:", err)
	}

	ln, err := net.Listen("tcp", config.AddrPort().String())
	if err != nil {
		logger.Fatalln("cannot listen requested address:", err)
	}

	if config.CertFile != "" && config.CertKey != "" {
		cert, err := tls.LoadX509KeyPair(config.CertFile, config.CertKey)
		if err != nil {
			logger.Fatalln("cannot load certificate files from config:", err)
		}

		ln = tls.NewListener(ln, &tls.Config{Certificates: []tls.Certificate{cert}})
	}

	for _, dir := range []*string{
		&config.ContentDir,
		&config.ThemeDir,
		&config.StaticDir,
	} {
		if *dir, err = filepath.Abs(filepath.Clean(*dir)); err != nil {
			logger.Fatalf("cannot format '%s' into absolute path: %s", *dir, err)
		}

		if _, err = os.Stat(*dir); err != nil {
			if errors.Is(err, os.ErrExist) {
				return
			}

			if !errors.Is(err, os.ErrNotExist) {
				logger.Fatalf("cannot check '%s' path: %v", *dir, err)
			}

			if err = os.MkdirAll(*dir, os.ModePerm); err != nil {
				logger.Fatalf("cannot create directory on '%s': %v", *dir, err)
			}
		}
	}

	ctx := context.Background()

	app, err := home.NewApp(logger, config)
	if err != nil {
		logger.Fatalln("cannot setup app:", err)
	}

	done := make(chan os.Signal, 1)
	signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)

	if cpuProfilePath != "" {
		cpuProfile, err := os.Create(cpuProfilePath)
		if err != nil {
			logger.Fatalln("could not create CPU profile:", err)
		}
		defer cpuProfile.Close()

		if err = pprof.StartCPUProfile(cpuProfile); err != nil {
			logger.Fatalln("could not start CPU profile:", err)
		}
		defer pprof.StopCPUProfile()
	}

	go func() {
		logger.Printf("starting server on %d...", config.Port)

		if err = app.Run(ln); err != nil {
			logger.Fatalln("cannot run app:", err)
		}
	}()

	<-done

	if err = app.Stop(ctx); err != nil {
		logger.Fatalln("failed shutdown server:", err)
	}

	if memProfilePath == "" {
		return
	}

	memProfile, err := os.Create(memProfilePath)
	if err != nil {
		logger.Fatalln("could not create memory profile:", err)
	}
	defer memProfile.Close()

	runtime.GC() // NOTE(toby3d): get up-to-date statistics

	if err = pprof.WriteHeapProfile(memProfile); err != nil {
		logger.Fatalln("could not write memory profile:", err)
	}
}
