package main

import (
	"embed"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"regexp"
	"strconv"
	"strings"
)

//go:embed templates/*.html
var templatesFS embed.FS

type ErrorPageData struct {
	StatusCode  int
	Title       string
	Description string
	ServerName  string
	ServerFlag  string
}

type NonErrorPageData struct {
	Hostname   string
	ServerName string
	ServerFlag string
}

var (
	serverName string
	serverFlag string
)

func main() {
	tmpl, err := template.ParseFS(templatesFS, "templates/*.html")
	if err != nil {
		log.Fatalf("Failed to parse templates: %v", err)
	}

	serverName = os.Getenv("SERVER_NAME")
	if serverName == "" {
		serverName = "unknown-server"
	}

	serverFlag = os.Getenv("SERVER_FLAG")
	if serverFlag == "" {
		serverFlag = "🇬🇧"
	}

	http.HandleFunc("/", defaultHandler(tmpl))

	http.HandleFunc("/error/", errorHandler(tmpl))

	http.HandleFunc("/maintenance", maintenanceHandler(tmpl))

	log.Println("Starting Server at :6660")
	log.Fatal(http.ListenAndServe(":6660", nil))
}

func defaultHandler(tmpl *template.Template) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		hostname := r.Host

		data := NonErrorPageData{
			Hostname:   hostname,
			ServerName: serverName,
			ServerFlag: serverFlag,
		}

		w.WriteHeader(http.StatusNotFound)
		w.Header().Set("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate")
		w.Header().Set("Expires", "0")
		w.Header().Set("Pragma", "no-cache")

		if err := tmpl.ExecuteTemplate(w, "default.html", data); err != nil {
			log.Printf("Template execution error: %v", err)
		}
	}
}

func maintenanceHandler(tmpl *template.Template) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		hostname := r.Host

		data := NonErrorPageData{
			Hostname:   hostname,
			ServerName: serverName,
			ServerFlag: serverFlag,
		}

		w.WriteHeader(http.StatusServiceUnavailable)
		w.Header().Set("Retry-After", "43200")
		w.Header().Set("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate")
		if err := tmpl.ExecuteTemplate(w, "maintenance.html", data); err != nil {
			log.Printf("Template execution error: %v", err)
		}
	}
}

func errorHandler(tmpl *template.Template) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		path := r.URL.Path
		const prefix = "/error/"
		const suffix = ".html"

		codeStr := strings.TrimPrefix(path, prefix)
		codeStr = strings.TrimSuffix(codeStr, suffix)

		match, _ := regexp.MatchString(`^\d{3}$`, codeStr)

		var statusCode int
		if match {
			code, err := strconv.Atoi(codeStr)
			if err == nil {
				statusCode = code
			} else {
				statusCode = 503
			}
		} else {
			statusCode = 503
		}

		msg, ok := statusMessages[statusCode]
		if !ok {
			msg = struct {
				Title       string
				Description string
			}{
				Title:       fmt.Sprintf("Error %d", statusCode),
				Description: "Something went wrong on our end — but we're not sure what.  Try again later.",
			}
		}

		data := ErrorPageData{
			StatusCode:  statusCode,
			Title:       msg.Title,
			Description: msg.Description,
			ServerName:  serverName,
			ServerFlag:  serverFlag,
		}

		w.WriteHeader(data.StatusCode)
		w.Header().Set("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate")
		err := tmpl.ExecuteTemplate(w, "error.html", data)
		if err != nil {
			log.Printf("Template Execution Error: %v", err)
		}
	}
}

var statusMessages = map[int]struct {
	Title       string
	Description string
}{
	400: {
		"Oops! Something's not quite right.",
		"We couldn't understand your request. Maybe check the URL or try again?",
	},
	401: {
		"Hold on — who goes there?",
		"You need to be logged in to see this page.",
	},
	403: {
		"Access Denied",
		"Looks like you don't have permission to access this content.",
	},
	404: {
		"Page Not Found",
		"We looked everywhere but couldn’t find that page. Double-check the link?",
	},
	500: {
		"Something went wrong",
		"The server ran into a problem. Try again in a bit — we're probably already fixing it!",
	},
	502: {
		"Bad Gateway",
		"We got a strange response from an upstream server. Not your fault!",
	},
	503: {
		"Service Unavailable",
		"The service is down for maintenance or having a hiccup. Please check back soon.",
	},
	504: {
		"Gateway Timeout",
		"Something took too long to respond. You can try refreshing in a moment.",
	},
	418: {
		"I'm a teapot ☕",
		"This server refuses to brew coffee because it is, in fact, a teapot.",
	},
	429: {
		"Too Many Requests",
		"Whoa there! You’re sending too many requests. Please slow down.",
	},
}
