// Copyright (C) 2022 Marius Schellenberger

package main

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

	//_ "net/http/pprof"
	"os"
	"os/signal"
	"syscall"
	"time"

	"cachefs/pkg/fs"
	"cachefs/pkg/srv"

	"github.com/go-logr/logr"
	"k8s.io/klog/v2"
	"k8s.io/klog/v2/klogr"
)

var (
	version string

	src          string
	dst          string
	metadata     string
	listenHttp   string
	listenWebdav string
	listenCache  string
	quota        int64
	max          int
	bs           int
	block        bool

	log logr.Logger
)

const gib = 1024 * 1024 * 1024

func main() {
	klog.InitFlags(nil)
	flag.StringVar(&src, "src", "", "url path to source files (example: file:///mnt/nfs)")
	flag.StringVar(&dst, "dst", "", "url path to cache files (example: file:///mnt/cache)")
	flag.StringVar(&metadata, "data", "", "path to metadata file")
	flag.StringVar(&listenHttp, "listen", "127.0.0.1:8080", "listen addr:port")
	flag.StringVar(&listenWebdav, "webdav", "", "listen addr:port for webdav")
	flag.StringVar(&listenCache, "cache", "", "listen addr:port for cache only")
	flag.IntVar(&max, "max", 1, "max parallel preloads")
	flag.IntVar(&bs, "bs", -1, "tune preload buffer size in bytes (default: 8192)")
	flag.Int64Var(&quota, "quota", 1, "max disk usage quota for the dst cache in GiB")
	flag.BoolVar(&block, "block", true, "block until sftp is connected")
	flag.Parse()
	//go func() {
	//	http.ListenAndServe("127.0.0.1:7777", nil)
	//}()

	log = klogr.New().WithName("main")
	log.Info("starting cachefs", "version", version)

	if fs.SetBufferSize(bs) {
		log.V(2).Info("changed preload buffer size", "size", bs)
	}

	filesystem, err := fs.NewFS(
		quota*gib,
		max,
		src,
		os.Getenv("SRC_KEY"),
		dst,
		os.Getenv("DST_KEY"),
		metadata,
		block,
		klogr.New().WithName("fs"),
	)
	if err != nil {
		klog.Fatalf("init failed: %s", err)
	}
	slog := klogr.New().WithName("srv")
	s := &http.Server{
		Addr: listenHttp,
		Handler: srv.NewFileServer(
			filesystem,
			slog,
		),
	}
	go func() {
		log.Info("starting main server", "addr", listenHttp)
		err := s.ListenAndServe()
		if err != nil && err != http.ErrServerClosed {
			klog.Fatalf("init failed: %s", err)
		}
	}()
	var (
		dav   *http.Server
		cache *http.Server
	)
	if listenWebdav != "" {
		log.Info("starting webdav server", "addr", listenWebdav)
		dav = &http.Server{
			Addr: listenWebdav,
			Handler: srv.NewWebDavServer(
				fs.NewWebDavFS(filesystem),
				slog.WithName("webdav"),
			),
		}
		go func() {
			err := dav.ListenAndServe()
			if err != nil && err != http.ErrServerClosed {
				klog.Fatalf("init failed: %s", err)
			}
		}()
	}
	if listenCache != "" {
		log.Info("starting cache server", "addr", listenCache)
		cache = &http.Server{
			Addr: listenCache,
			Handler: srv.NewCacheServer(
				filesystem,
				slog.WithName("cache"),
			),
		}
		go func() {
			err := cache.ListenAndServe()
			if err != nil && err != http.ErrServerClosed {
				klog.Fatalf("init failed: %s", err)
			}
		}()
	}
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
	<-sigs
	log.Info("cachefs shutdown initiated")
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
	s.Shutdown(ctx)
	cancel()
	if dav != nil {
		ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
		dav.Shutdown(ctx)
		cancel()
	}
	if cache != nil {
		ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
		cache.Shutdown(ctx)
		cancel()
	}
	filesystem.Close()
	<-filesystem.Done()
	log.Info("cachefs shutdown completed")
	os.Exit(0)
}
