package main

import (
	"io"
	"log"
	"net"
	"net/http"
	"net/url"
	"path"
	"strings"

	"gfx.cafe/open/gun"
)

var Config struct {
	Port string
	Root string

	Remote string
}

func init() {
	gun.Load(&Config)
	if Config.Port == "" {
		Config.Port = "8080"
	}
	if Config.Root == "" {
		Config.Root = "dist"
	}
	if Config.Remote == "" {
		Config.Remote = "https://beta.lifeto.co"
	}

	Config.Root = path.Clean(Config.Root)
}

var hopHeaders = []string{
	"Connection",
	"Keep-Alive",
	"Proxy-Authenticate",
	"Proxy-Authorization",
	"Te",
	"Trailers",
	"Transfer-Encoding",
	"Upgrade",
}

func appendHostToXForwardHeader(header http.Header, host string) {
	if prior, ok := header["X-Forwarded-For"]; ok {
		host = strings.Join(prior, ", ") + ", " + host
	}
	header.Set("X-Forwarded-For", host)
}

func copyHeader(dst, src http.Header) {
	for k, vv := range src {
		for _, v := range vv {
			dst.Add(k, v)
		}
	}
}

func delHopHeaders(header http.Header) {
	for _, h := range hopHeaders {
		header.Del(h)
	}
}

type Handler struct {
	p ProxyHandler
}

func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if strings.HasPrefix(r.URL.Path, "/lifeto") {
		http.StripPrefix("/lifeto", &h.p).ServeHTTP(w, r)
		return
	}
	h.handleSite(w, r)
}

type ProxyHandler struct {
	c http.Client
}

func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	r.RequestURI = ""
	if clientIP, _, err := net.SplitHostPort(r.RemoteAddr); err == nil {
		appendHostToXForwardHeader(r.Header, clientIP)
	}
	r.URL, _ = url.Parse(Config.Remote + r.URL.Path)
	r.Host = r.URL.Host

	resp, err := h.c.Do(r)
	if err != nil {
		http.Error(w, "Server Error", http.StatusInternalServerError)
		log.Println("ServeHTTP:", err)
		return
	}
	defer resp.Body.Close()
	delHopHeaders(resp.Header)
	copyHeader(w.Header(), resp.Header)
	w.WriteHeader(resp.StatusCode)
	io.Copy(w, resp.Body)
}

func (h *Handler) handleSite(w http.ResponseWriter, r *http.Request) {
	http.FileServer(http.Dir(Config.Root)).ServeHTTP(w, r)
}

func main() {
	log.Printf("starting with config: %+v", Config)
	http.ListenAndServe(":"+Config.Port, &Handler{})
}