package main

import (
	"database/sql"
	"embed"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"html/template"
	"io"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"

	"hg.lukegb.com/lukegb/depot/web/barf/frontend/barfdb"

	_ "github.com/mattn/go-sqlite3"
)

var (
	serve      = flag.String("serve", ":11111", "Port number.")
	samBackend = flag.String("sam_backend", "http://localhost:11316", "Sam backend.")
	dbPath     = flag.String("db_path", "./db.db", "Path to database.")
)

//go:embed index.html
var indexTmplBytes []byte

//go:embed static
var staticFS embed.FS

type application struct {
	db        *barfdb.DB
	indexTmpl *template.Template
}

type dataset struct {
	CurrentPhase                int    `json:"currentPhase"`
	CurrentPhaseDialog          int    `json:"currentPhaseDialog"`
	AudioEnabled                bool   `json:"audioEnabled"`
	Name                        string `json:"name"`
	DiscordUsername             string `json:"discordUsername"`
	ReceiveEmail                bool   `json:"receiveEmail"`
	Email                       string `json:"email"`
	DateSat31August             string `json:"dateSat31August"`
	DateSat7September           string `json:"dateSat7September"`
	ActivityEscapeRoom          string `json:"activityEscapeRoom"`
	ActivityPub                 string `json:"activityPub"`
	ActivityMeanGirlsTheMusical string `json:"activityMeanGirlsTheMusical"`
	ActivityKaraoke             string `json:"activityKaraoke"`
	AccommodationRequired       bool   `json:"accommodationRequired"`
	TravelCosts                 bool   `json:"travelCosts"`
	Misc                        string `json:"misc"`
}

func (a *application) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/" {
		rw.WriteHeader(http.StatusNotFound)
		fmt.Fprintf(rw, "Sorry, you need a key to access this application.\n")
		return
	}

	key := r.URL.Path[1:]
	if r.Method == "PUT" {
		body, err := io.ReadAll(r.Body)
		if err != nil {
			log.Printf("failed to read PUT body: %v", err)
			http.Error(rw, "reading body failed", http.StatusInternalServerError)
			return
		}
		var response barfdb.Dataset
		if err := json.Unmarshal(body, &response); err != nil {
			log.Printf("failed to unmarshal PUT dataset: %v", err)
			http.Error(rw, "unmarshalling body failed", http.StatusBadRequest)
			return
		}
		if err := a.db.SaveResponseForKey(r.Context(), key, &response); errors.Is(err, barfdb.ErrNoSuchKey) {
			rw.WriteHeader(http.StatusNotFound)
			fmt.Fprintf(rw, "Sorry, you need a key to access this application.\n")
			return
		} else if err != nil {
			log.Printf("SaveResponseKeyFor %v: %v", key, err)
			http.Error(rw, "saving response failed", http.StatusInternalServerError)
			return
		}
		rw.WriteHeader(http.StatusNoContent)
		return
	}

	response, err := a.db.GetResponseForKey(r.Context(), key)
	if errors.Is(err, barfdb.ErrNoSuchKey) {
		rw.WriteHeader(http.StatusNotFound)
		fmt.Fprintf(rw, "Sorry, you need a key to access this application.\n")
		return
	} else if err != nil {
		log.Printf("getting responses: %v", err)
		http.Error(rw, "internal error getting responses", http.StatusInternalServerError)
		return
	}

	if response.CurrentPhase >= 6 {
		// Make sure people who have saved _completion_ start at the beginning.
		response.CurrentPhase = 0
		response.CurrentPhaseDialog = 0
	}
	responseJSONBytes, err := json.Marshal(response)
	if err != nil {
		log.Printf("couldn't reserialise database key %v: %v", key, err)
		http.Error(rw, "something went wrong with reserialisation...", http.StatusInternalServerError)
		return
	}
	responseJSONStr := string(responseJSONBytes)

	rw.Header().Set("Content-type", "text/html")
	a.indexTmpl.Execute(rw, template.JS(responseJSONStr))
}

func main() {
	flag.Parse()

	indexTmpl, err := template.New("index").Parse(string(indexTmplBytes))
	if err != nil {
		log.Fatalf("parsing index template: %v", err)
	}

	sdb, err := sql.Open("sqlite3", *dbPath)
	if err != nil {
		log.Fatal(err)
	}
	defer sdb.Close()
	db := barfdb.New(sdb)

	app := &application{db: db, indexTmpl: indexTmpl}

	http.Handle("/", app)

	http.Handle("/static/", http.FileServer(http.FS(staticFS)))

	samBackendPath, err := url.Parse(*samBackend)
	if err != nil {
		log.Fatalf("parsing -sam_backend=%q: %v", *samBackend, err)
	}
	be := httputil.NewSingleHostReverseProxy(samBackendPath)
	http.Handle("/sam", be)

	log.Printf("serving on %v", *serve)
	log.Fatal(http.ListenAndServe(*serve, nil))
}