package main

import (
	"bytes"
	"io"
	"io/ioutil"
	"log"
	"mime"
	"net/http"
	"net/url"
	"path"
	"strings"

	"github.com/gin-gonic/gin"
)

var (
	BaseURL   = "/"
	DevProxy  string
	indexTmpl []byte
)

func getIndexHtml(w io.Writer, file io.Reader) []byte {
	if indexTmpl, err := ioutil.ReadAll(file); err != nil {
		log.Println("Cannot read whole index.html: ", err)
		return nil
	} else {
		good_base := []byte(path.Clean(path.Join(BaseURL+"/", "nuke"))[:len(path.Clean(path.Join(BaseURL+"/", "nuke")))-4])

		indexTmpl = bytes.Replace(
			bytes.Replace(
				bytes.Replace(
					indexTmpl,
					[]byte(`<base href="/">`),
					[]byte(`<base href="`+string(good_base)+`">`),
					-1,
				),
				[]byte("\"/_app/"),
				append([]byte("\""), append(good_base, '_', 'a', 'p', 'p', '/')...),
				-1,
			),
			[]byte("base: \""),
			append([]byte("base: \""), good_base[:len(good_base)-1]...),
			-1,
		)

		w.Write(indexTmpl)
		return indexTmpl
	}
}

func serveOrReverse(forced_url string, baseURL string) func(c *gin.Context) {
	return func(c *gin.Context) {
		if DevProxy != "" {
			if u, err := url.Parse(DevProxy); err != nil {
				http.Error(c.Writer, err.Error(), http.StatusInternalServerError)
			} else {
				if forced_url != "" {
					u.Path = path.Join(u.Path, forced_url)
				} else {
					u.Path = path.Join(u.Path, strings.TrimPrefix(c.Request.URL.Path, baseURL))
				}

				if r, err := http.NewRequest(c.Request.Method, u.String(), c.Request.Body); err != nil {
					http.Error(c.Writer, err.Error(), http.StatusInternalServerError)
				} else if resp, err := http.DefaultClient.Do(r); err != nil {
					http.Error(c.Writer, err.Error(), http.StatusBadGateway)
				} else {
					defer resp.Body.Close()

					for key := range resp.Header {
						c.Writer.Header().Add(key, resp.Header.Get(key))
					}
					c.Writer.WriteHeader(resp.StatusCode)

					if r.URL.Path == path.Join(u.Path, "/") {
						getIndexHtml(c.Writer, resp.Body)
					} else {
						io.Copy(c.Writer, resp.Body)
					}
				}
			}
		} else {
			if forced_url != "" {
				c.Request.URL.Path = forced_url
			} else {
				c.Request.URL.Path = strings.TrimPrefix(c.Request.URL.Path, baseURL)
			}

			if c.Request.URL.Path == "/" {
				if len(indexTmpl) == 0 {
					if file, err := Assets.Open("index.html"); err != nil {
						log.Println("Unable to open index.html: ", err)
					} else {
						defer file.Close()

						indexTmpl = getIndexHtml(c.Writer, file)
					}
				} else {
					c.Writer.Write(indexTmpl)
				}
			} else if baseURL != "/" {
				if file, err := Assets.Open(c.Request.URL.Path); err != nil {
					http.FileServer(Assets).ServeHTTP(c.Writer, c.Request)
				} else {
					defer file.Close()

					c.Writer.Header().Add("Content-Type", mime.TypeByExtension(path.Ext(c.Request.URL.Path)))

					getIndexHtml(c.Writer, file)
				}
			} else {
				http.FileServer(Assets).ServeHTTP(c.Writer, c.Request)
			}
		}
	}
}

func declareStaticRoutes(router *gin.RouterGroup, baseURL string) {
	router.GET("/", serveOrReverse("", baseURL))
	router.GET("/exercices", serveOrReverse("/", baseURL))
	router.GET("/exercices/*_", serveOrReverse("/", baseURL))
	router.GET("/export", serveOrReverse("/", baseURL))
	router.GET("/teams", serveOrReverse("/", baseURL))
	router.GET("/teams/*_", serveOrReverse("/", baseURL))
	router.GET("/themes", serveOrReverse("/", baseURL))
	router.GET("/themes/*_", serveOrReverse("/", baseURL))
	router.GET("/_app/*_", serveOrReverse("", baseURL))

	router.GET("/.svelte-kit/*_", serveOrReverse("", baseURL))
	router.GET("/node_modules/*_", serveOrReverse("", baseURL))
	router.GET("/@vite/*_", serveOrReverse("", baseURL))
	router.GET("/@fs/*_", serveOrReverse("", baseURL))
	router.GET("/@id/*_", serveOrReverse("", baseURL))
	router.GET("/src/*_", serveOrReverse("", baseURL))
}
