package server

import (
	_ "embed"
	"encoding/json"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"

	"github.com/benbusby/farside/db"
	"github.com/benbusby/farside/services"
)

//go:embed index.html
var indexHTML string

//go:embed route.html
var routeHTML string

type indexData struct {
	LastUpdated time.Time
	ServiceList []services.Service
}

type routeData struct {
	InstanceURL string
}

func home(w http.ResponseWriter, r *http.Request) {
	serviceList := db.GetServiceList()
	data := indexData{
		LastUpdated: db.LastUpdate,
		ServiceList: serviceList,
	}

	tmpl, err := template.New("").Parse(indexHTML)
	if err != nil {
		log.Println(err)
		http.Error(w, "Error parsing template", http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "text/html")

	err = tmpl.Execute(w, data)
	if err != nil {
		log.Println(err)
		http.Error(w, "Error executing template", http.StatusInternalServerError)
	}
}

func state(w http.ResponseWriter, r *http.Request) {
	storedServices := db.GetServiceList()
	jsonData, _ := json.Marshal(storedServices)
	w.Header().Set("Content-Type", "application/json")
	_, _ = w.Write(jsonData)
}

func baseRouting(w http.ResponseWriter, r *http.Request) {
	routing(w, r, false, r.URL.RawQuery)
}

func jsRouting(w http.ResponseWriter, r *http.Request) {
	r.URL.Path = strings.Replace(r.URL.Path, "/_", "", 1)
	routing(w, r, true, r.URL.RawQuery)
}

func routing(w http.ResponseWriter, r *http.Request, jsEnabled bool, query string) {
	value := r.PathValue("routing")
	if len(value) == 0 {
		value = r.URL.Path
	}

	parsedURL, _ := url.Parse(value)
	path := strings.TrimPrefix(parsedURL.Path, "/")
	segments := strings.Split(path, "/")

	if len(segments[0]) == 0 {
		http.Redirect(w, r, "", http.StatusTemporaryRedirect)
	}

	target, err := services.MatchRequest(segments[0])
	if err != nil {
		errMsg := fmt.Sprintf("No routing found for '%s'", segments[0])
		log.Printf("Error during match request: %v\n", err)
		http.Error(w, errMsg, http.StatusBadRequest)
		return
	}

	var servicePath string
	if target == "breezewiki" {
		// Breezewiki requires the subdomain of the instance to be
		// preserved for correct routing
		splitDomain := strings.Split(path, ".")
		if len(splitDomain) > 2 {
			servicePath = strings.Split(path, ".")[0]
		}
	}

	instance, err := db.GetInstance(target, servicePath)
	if err != nil {
		log.Printf("Error fetching instance from db: %v\n", err)
		http.Error(
			w,
			"Error fetching instance for "+target,
			http.StatusInternalServerError)
		return
	}

	if len(segments) > 1 {
		targetPath := strings.Join(segments[1:], "/")
		instance = instance + "/" + targetPath
	}

	w.Header().Set("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0")
	w.Header().Set("Pragma", "no-cache")
	w.Header().Set("Expires", "0")

	instance += fmt.Sprintf("?%s", query)
	if jsEnabled {
		data := routeData{
			InstanceURL: instance,
		}
		tmpl, _ := template.New("").Parse(routeHTML)
		w.Header().Set("Content-Type", "text/html")
		_ = tmpl.Execute(w, data)
	} else {
		http.Redirect(w, r, instance, http.StatusFound)
	}
}

func RunServer() {
	mux := http.NewServeMux()
	mux.HandleFunc("/{$}", home)
	mux.HandleFunc("/state/{$}", state)
	mux.HandleFunc("/{routing...}", baseRouting)
	mux.HandleFunc("/_/{routing...}", jsRouting)

	port := os.Getenv("FARSIDE_PORT")
	if len(port) == 0 {
		port = "4001"
	}

	log.Println("Starting server on http://localhost:" + port)

	err := http.ListenAndServe(":"+port, mux)
	if err != nil {
		log.Fatal(err)
	}
}