package web

import (
	"html/template"
	"io"
	"sort"
	"strings"

	"encoding/json"

	"net/http"
	"path"

	"github.com/amir20/dozzle/internal/auth"
	"github.com/amir20/dozzle/internal/profile"
	"github.com/rs/zerolog/log"
)

func (h *handler) index(w http.ResponseWriter, req *http.Request) {
	path := req.URL.Path
	_, err := h.content.Open(path)
	if err == nil && req.URL.Path != "" && req.URL.Path != "/" {
		w.Header().Set("Cache-Control", "max-age=31536000, immutable")
		fileServer.ServeHTTP(w, req)
	} else {
		h.executeTemplate(w, req)
	}
}

func (h *handler) executeTemplate(w http.ResponseWriter, req *http.Request) {
	base := ""
	if h.config.Base != "/" {
		base = h.config.Base
	}

	user := auth.UserFromContext(req.Context())

	// Handle unauthorized cases early
	if user == nil {
		switch h.config.Authorization.Provider {
		case FORWARD_PROXY:
			log.Error().Msg("Unable to find remote user. Please check your proxy configuration. Expecting headers Remote-Email, Remote-User, Remote-Name.")
			log.Debug().Str("url", req.URL.String()).Msg("Dumping all headers for request")
			for k, v := range req.Header {
				log.Debug().Strs(k, v).Send()
			}
			http.Error(w, "Unauthorized user", http.StatusUnauthorized)
			return
		case SIMPLE:
			if req.URL.Path != "login" {
				log.Debug().Str("url", req.URL.String()).Msg("Redirecting to login page")
				http.Redirect(w, req, path.Clean(h.config.Base+"/login")+"?redirectUrl=/"+req.URL.String(), http.StatusTemporaryRedirect)
				return
			}
		}
	}

	config := map[string]interface{}{
		"base": base,
	}

	// Build full config when authorized (no auth or authenticated user)
	if h.config.Authorization.Provider == NONE || user != nil {
		hosts := h.hostService.Hosts()
		sort.Slice(hosts, func(i, j int) bool {
			return hosts[i].Name < hosts[j].Name
		})

		config["authProvider"] = h.config.Authorization.Provider
		config["version"] = h.config.Version
		config["hostname"] = h.config.Hostname
		config["mode"] = h.config.Mode
		config["hosts"] = hosts
		config["disableAvatars"] = h.config.DisableAvatars
		config["releaseCheckMode"] = h.config.ReleaseCheckMode
		config["enableShell"] = h.config.EnableShell
		config["enableActions"] = h.config.EnableActions
		config["enableDownload"] = true

		if user != nil {
			config["enableShell"] = h.config.EnableShell && user.Roles.Has(auth.Shell)
			config["enableActions"] = h.config.EnableActions && user.Roles.Has(auth.Actions)
			config["enableDownload"] = user.Roles.Has(auth.Download)
			config["user"] = user
		}

		if h.config.Authorization.Provider == FORWARD_PROXY && strings.TrimSpace(h.config.Authorization.LogoutUrl) != "" {
			config["logoutUrl"] = strings.TrimSpace(h.config.Authorization.LogoutUrl)
		}
	}

	profileUsername := profile.DefaultUsername
	if user != nil {
		profileUsername = user.Username
	}

	if loadedProfile, err := profile.Load(profileUsername); err == nil {
		config["profile"] = loadedProfile
	} else {
		config["profile"] = struct{}{}
	}

	data := map[string]interface{}{
		"Config":   config,
		"Dev":      h.config.Dev,
		"Manifest": h.readManifest(),
		"Base":     base,
	}
	file, err := h.content.Open("index.html")
	if err != nil {
		log.Fatal().Err(err).Msg("Could not open index.html")
	}
	bytes, err := io.ReadAll(file)
	if err != nil {
		log.Fatal().Err(err).Msg("Could not read index.html")
	}
	tmpl, err := template.New("index.html").Funcs(template.FuncMap{
		"marshal": func(v interface{}) template.JS {
			var p []byte
			if h.config.Dev {
				p, _ = json.MarshalIndent(v, "", "  ")
			} else {
				p, _ = json.Marshal(v)
			}
			return template.JS(p)
		},
	}).Parse(string(bytes))
	if err != nil {
		log.Fatal().Err(err).Msg("Could not parse index.html")
	}

	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	err = tmpl.Execute(w, data)
	if err != nil {
		log.Fatal().Err(err).Msg("Could not execute index.html")
	}
}

func (h *handler) readManifest() map[string]interface{} {
	if h.config.Dev {
		return map[string]interface{}{}
	} else {
		file, err := h.content.Open(".vite/manifest.json")
		if err != nil {
			// this should only happen during test. In production, the file is embedded in the binary and checked in main.go
			return map[string]interface{}{}
		}
		bytes, err := io.ReadAll(file)
		if err != nil {
			log.Fatal().Err(err).Msg("Could not read .vite/manifest.json")
		}
		var manifest map[string]interface{}
		err = json.Unmarshal(bytes, &manifest)
		if err != nil {
			log.Fatal().Err(err).Msg("Could not unmarshal .vite/manifest.json")
		}
		return manifest
	}
}
