package main

import (
	"context"
	"embed"
	"encoding/json"
	"errors"
	"flag"
	"io/fs"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"git.gensokyo.uk/yonah/monstersirenfetch"
)

const shutdownTimeout = 5 * time.Second

var (
	flagVerbose bool
	flagAddr    string
)

func init() {
	flag.BoolVar(&flagVerbose, "v", false, "Increase log verbosity")
	flag.StringVar(&flagAddr, "a", ":3000",
		`TCP address for the server to listen on, in the form "host:port".`)
}

var (
	//go:embed all:static
	staticFS embed.FS
)

func main() {
	log.SetFlags(0)
	log.SetPrefix("msrserve: ")
	flag.Parse()

	mux := http.NewServeMux()
	mux.HandleFunc("/", http.FileServerFS(mustSub(staticFS, "static")).ServeHTTP)

	mux.HandleFunc("/{$}", serveAppShell)
	mux.HandleFunc("/about/{$}", serveAppShell)
	mux.HandleFunc("/music/", serveAppShell)
	mux.HandleFunc("/info/{$}", serveAppShell)
	mux.HandleFunc("/contact/{$}", serveAppShell)

	mux.HandleFunc("GET /api/", handleAPINew(mustReadJSON[*monstersirenfetch.Metadata](flagMetadataPath)))
	mux.HandleFunc("/data/", handleDataNew(flagFetchDirPath))

	s := http.Server{Addr: flagAddr, Handler: mux}
	sig := make(chan os.Signal, 2)
	go func() {
		defer signal.Stop(sig)
		v := <-sig
		ctx, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
		defer cancel()
		log.Print(v)
		if err := s.Shutdown(ctx); err != nil {
			log.Fatal(err)
		}
	}()

	signal.Notify(sig, os.Interrupt, syscall.SIGTERM)
	if err := s.ListenAndServe(); err != nil {
		if !errors.Is(err, http.ErrServerClosed) {
			log.Fatal(err)
		}
	}
}

func serveAppShell(w http.ResponseWriter, r *http.Request) {
	http.ServeFileFS(w, r, staticFS, "static/appShell.html")
}

func writeResp(writer http.ResponseWriter, data []byte) bool {
	if _, err := writer.Write(data); err != nil {
		verboseln(err)
		return false
	}
	return true
}

func mustMarshalJSON(v any) []byte {
	if data, err := json.Marshal(v); err != nil {
		log.Fatal(err)
		return nil
	} else {
		return data
	}
}

func mustReadJSON[T any](pathname string) T {
	var v T
	if r, err := os.OpenFile(pathname, os.O_RDONLY, 0); err != nil {
		log.Fatal(err)
	} else if err = json.NewDecoder(r).Decode(&v); err != nil {
		log.Fatal(err)
	} else if err = r.Close(); err != nil {
		log.Fatal(err)
	}
	return v
}

func mustSub(fsys fs.FS, dir string) fs.FS {
	if sub, err := fs.Sub(fsys, dir); err != nil {
		log.Fatal(err)
		return nil
	} else {
		return sub
	}
}

func verboseln(v ...any) {
	if flagVerbose {
		log.Println(v...)
	}
}
