package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"path"
	"strings"
	"syscall"
	"time"

	"srs.epita.fr/fic-server/settings"
)

func main() {
	var bind = flag.String("bind", "127.0.0.1:8080", "Bind port/socket")
	var prefix = flag.String("prefix", "/", "Request path prefix to strip (from proxy)")
	var teamsDir = flag.String("teams", "./TEAMS/", "Base directory where find existing teams")
	flag.StringVar(&settings.SettingsDir, "settings", settings.SettingsDir, "Base directory where read settings")
	flag.StringVar(&SettingsDistDir, "settingsDist", SettingsDistDir, "Directory where place settings to distribute")
	flag.StringVar(&startedFile, "startedFile", startedFile, "Path to the file to create/remove whether or not the challenge is running")
	flag.StringVar(&SubmissionDir, "submission", "./submissions/", "Base directory where save submissions")
	var simulator = flag.String("simulator", "", "Team to simulate (for development only)")
	flag.StringVar(&staticDir, "static", staticDir, "Set to serve pages as well (for development only, use with -simulator)")
	flag.Parse()

	log.SetPrefix("[frontend] ")

	startedFile = path.Clean(startedFile)
	SubmissionDir = path.Clean(SubmissionDir)
	TmpSubmissionDir = path.Join(SubmissionDir, ".tmp")

	log.Println("Creating submission directory...")
	if _, err := os.Stat(TmpSubmissionDir); os.IsNotExist(err) {
		if err = os.MkdirAll(TmpSubmissionDir, 0700); err != nil {
			log.Fatal("Unable to create submission directory:", err)
		}
	}

	log.Println("Creating settingsDist directory...")
	if _, err := os.Stat(SettingsDistDir); os.IsNotExist(err) {
		if err = os.MkdirAll(SettingsDistDir, 0755); err != nil {
			log.Fatal("Unable to create settingsdist directory:", err)
		}
	}

	*prefix = strings.TrimRight(*prefix, "/")

	// Load configuration
	settings.LoadAndWatchSettings(path.Join(settings.SettingsDir, settings.SettingsFile), reloadSettings)

	// Register handlers
	http.Handle(fmt.Sprintf("%s/chname/", *prefix), http.StripPrefix(fmt.Sprintf("%s/chname/", *prefix), submissionTeamChecker{"name change", ChNameHandler, *teamsDir}))
	http.Handle(fmt.Sprintf("%s/issue/", *prefix), http.StripPrefix(fmt.Sprintf("%s/issue/", *prefix), submissionTeamChecker{"issue", IssueHandler, *teamsDir}))
	http.Handle(fmt.Sprintf("%s/openhint/", *prefix), http.StripPrefix(fmt.Sprintf("%s/openhint/", *prefix), submissionTeamChecker{"opening hint", HintHandler, *teamsDir}))
	http.Handle(fmt.Sprintf("%s/wantchoices/", *prefix), http.StripPrefix(fmt.Sprintf("%s/wantchoices/", *prefix), submissionTeamChecker{"wantint choices", WantChoicesHandler, *teamsDir}))
	http.Handle(fmt.Sprintf("%s/registration/", *prefix), http.StripPrefix(fmt.Sprintf("%s/registration/", *prefix), submissionChecker{"registration", RegistrationHandler}))
	http.Handle(fmt.Sprintf("%s/resolution/", *prefix), http.StripPrefix(fmt.Sprintf("%s/resolution/", *prefix), ResolutionHandler{}))
	http.Handle(fmt.Sprintf("%s/submission/", *prefix), http.StripPrefix(fmt.Sprintf("%s/submission/", *prefix), submissionTeamChecker{"submission", SubmissionHandler, *teamsDir}))

	if *simulator != "" {
		if _, err := os.Stat(path.Join(*teamsDir, *simulator)); os.IsNotExist(err) {
			log.Printf("WARNING: Team '%s' doesn't exist yet in %s.", *simulator, *teamsDir)
		}

		// Serve team files
		http.Handle(fmt.Sprintf("%s/wait.json", *prefix), http.StripPrefix(*prefix, http.FileServer(http.Dir(path.Join(*teamsDir, *simulator)))))
		http.Handle(fmt.Sprintf("%s/my.json", *prefix), http.StripPrefix(*prefix, TeamMyServer{path.Join(*teamsDir, *simulator)}))

		// Serve generated content
		http.Handle(fmt.Sprintf("%s/teams.json", *prefix), http.StripPrefix(*prefix, http.FileServer(http.Dir(*teamsDir))))
		http.Handle(fmt.Sprintf("%s/themes.json", *prefix), http.StripPrefix(*prefix, http.FileServer(http.Dir(*teamsDir))))
		http.Handle(fmt.Sprintf("%s/stats.json", *prefix), http.StripPrefix(*prefix, http.FileServer(http.Dir(*teamsDir))))
		http.Handle(fmt.Sprintf("%s/settings.json", *prefix), http.StripPrefix(*prefix, http.FileServer(http.Dir(SettingsDistDir))))

		// Serve static assets
		http.Handle(fmt.Sprintf("%s/css/", *prefix), http.StripPrefix(*prefix, http.FileServer(http.Dir(staticDir))))
		http.Handle(fmt.Sprintf("%s/js/", *prefix), http.StripPrefix(*prefix, http.FileServer(http.Dir(staticDir))))

		http.Handle(fmt.Sprintf("%s/files/", *prefix), http.StripPrefix(*prefix, http.FileServer(http.Dir("FILES"))))

		// Serve index
		http.HandleFunc(fmt.Sprintf("%s/edit", *prefix), serveIndex)
		http.HandleFunc(fmt.Sprintf("%s/rank", *prefix), serveIndex)
		http.HandleFunc(fmt.Sprintf("%s/register", *prefix), serveIndex)
		http.HandleFunc(fmt.Sprintf("%s/rules", *prefix), serveIndex)
		http.HandleFunc(fmt.Sprintf("%s/videos", *prefix), serveIndex)
	}

	// Prepare graceful shutdown
	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM)

	srv := &http.Server{
		Addr: *bind,
	}

	// Serve pages
	go func() {
		log.Fatal(srv.ListenAndServe())
	}()
	log.Println(fmt.Sprintf("Ready, listening on %s", *bind))

	// Wait shutdown signal and touch timestamp
	ticker := time.NewTicker(time.Second)
	defer ticker.Stop()

loop:
	for {
		select {
		case <-interrupt:
			break loop
		case <-ticker.C:
			now := time.Now()
			os.Chtimes(SubmissionDir, now, now)
		}
	}


	log.Print("The service is shutting down...")
	srv.Shutdown(context.Background())
	log.Println("done")
}
