package main

import (
	"compress/gzip"
	"io"
	"log"
	"net/http"
	"strings"

	"chaosmith-site/blog"
)

type gzipResponseWriter struct {
	io.Writer
	http.ResponseWriter
}

func (w gzipResponseWriter) Write(b []byte) (int, error) {
	return w.Writer.Write(b)
}

func gzipHandler(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
			next.ServeHTTP(w, r)
			return
		}
		w.Header().Set("Content-Encoding", "gzip")
		w.Header().Del("Content-Length")
		gz := gzip.NewWriter(w)
		defer gz.Close()
		next.ServeHTTP(gzipResponseWriter{Writer: gz, ResponseWriter: w}, r)
	})
}

func cacheHandler(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if strings.HasPrefix(r.URL.Path, "/src/") ||
			strings.HasPrefix(r.URL.Path, "/content/images/") ||
			strings.HasPrefix(r.URL.Path, "/ABSTRACTS.") ||
			strings.HasPrefix(r.URL.Path, "/blog/og/") {
			w.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
		}
		next.ServeHTTP(w, r)
	})
}

type mimeWriter struct {
	http.ResponseWriter
	override string
	wrote    bool
}

func (m *mimeWriter) WriteHeader(code int) {
	if !m.wrote {
		m.wrote = true
		m.Header().Set("Content-Type", m.override)
	}
	m.ResponseWriter.WriteHeader(code)
}

func (m *mimeWriter) Write(b []byte) (int, error) {
	if !m.wrote {
		m.WriteHeader(200)
	}
	return m.ResponseWriter.Write(b)
}

func mimeHandler(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ext := strings.ToLower(r.URL.Path[strings.LastIndex(r.URL.Path, ".")+1:])
		var ct string
		switch ext {
		case "woff2":
			ct = "font/woff2"
		case "woff":
			ct = "font/woff"
		case "ttf":
			ct = "font/ttf"
		case "js":
			ct = "text/javascript"
		case "css":
			ct = "text/css"
		case "webp":
			ct = "image/webp"
		case "svg":
			ct = "image/svg+xml"
		case "ico":
			ct = "image/x-icon"
		}
		if ct != "" {
			next.ServeHTTP(&mimeWriter{ResponseWriter: w, override: ct}, r)
			return
		}
		next.ServeHTTP(w, r)
	})
}

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/ABSTRACTS.png", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, "ABSTRACTS.png")
	})
	mux.HandleFunc("/ABSTRACTS.webp", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, "ABSTRACTS.webp")
	})
	mux.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, "favicon.ico")
	})
	mux.HandleFunc("/favicon.svg", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, "favicon.svg")
	})
	mux.Handle("/src/", http.StripPrefix("/src/", http.FileServer(http.Dir("src"))))
	mux.Handle("/content/images/", http.StripPrefix("/content/images/", http.FileServer(http.Dir("content/images"))))

	if err := blog.RegisterRoutes(mux, "content/posts"); err != nil {
		log.Fatalf("blog: %v", err)
	}

	// Landing page — served by blog package so htmx can swap between sections
	mux.HandleFunc("/", blog.ServeLanding)

	log.Println("listening on :8000")
	http.ListenAndServe(":8000", mimeHandler(cacheHandler(gzipHandler(mux))))
}
