// SPDX-FileCopyrightText: 2021 Luke Granger-Brown <depot@lukegb.com>
//
// SPDX-License-Identifier: Apache-2.0

package fuphttp

import (
	"bytes"
	"io"
	"log"
	"mime"
	"net/http"
	"strings"

	"github.com/google/safehtml"
	"github.com/gorilla/mux"
	"gocloud.dev/gcerrors"
)

type viewTemplateData struct {
	Text     string
	Rendered safehtml.HTML
	RawURL   safehtml.TrustedResourceURL

	Filename string
	Meta     *Metadata

	ExpandBorder bool
}

func renderAsCode(mimeType string) bool {
	if strings.HasPrefix(mimeType, "text/") {
		return true
	}
	switch mimeType {
	case "application/json", "application/xml", "application/xhtml+xml", "application/x-csh", "application/x-sh":
		return true
	}
	return false
}

func (a *Application) view(rw http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	vars := mux.Vars(r)
	filename := vars["filename"]

	meta, err := metadata(ctx, a.storageBackend, filename)
	switch errorCode(err) {
	case gcerrors.NotFound:
		a.notFound(rw, r)
		return
	default:
		log.Printf("view(%q) metadata: %v", filename, err)
		a.internalError(rw, r)
		return
	case gcerrors.OK:
		// OK
	}

	// TODO(lukegb): Range header and conditionals?.
	rdr, err := a.storageBackend.NewReader(ctx, filename, nil)
	switch errorCode(err) {
	case gcerrors.NotFound:
		a.notFound(rw, r)
		return
	default:
		a.internalError(rw, r)
		return
	case gcerrors.OK:
		// OK
	}
	defer rdr.Close()

	content, err := io.ReadAll(rdr)
	if err != nil {
		log.Printf("view(%q): ReadAll: %v", filename, err)
		a.internalError(rw, r)
		return
	}

	if v := meta.Attributes.ModTime; !v.IsZero() {
		rw.Header().Set("Last-Modified", v.UTC().Format(http.TimeFormat))
	}

	data := viewTemplateData{
		Filename: filename,
		Text:     string(content),
		Meta:     meta,
	}
	data.RawURL, err = safehtml.TrustedResourceURLAppend(
		safehtml.TrustedResourceURLFromConstant("/raw/"),
		filename)
	if err != nil {
		log.Printf("view(%q): TrustedResourceURLAppend: %v", filename, err)
		a.internalError(rw, r)
		return
	}
	tmpl := a.viewTextTmpl

	parsedContentType, _, err := mime.ParseMediaType(meta.Attributes.ContentType)
	if err != nil {
		log.Printf("view(%q): parsing content type %v: %v", filename, meta.Attributes.ContentType, err)
		a.internalError(rw, r)
		return
	}

	// Is highlighting available?
	if bytes.Contains(content, []byte{0}) {
		// Probably binary.
		tmpl = a.viewBinaryTmpl
	} else if a.highlighter != nil {
		log.Printf("view(%q): content type %v", filename, parsedContentType)
		if parsedContentType == "text/markdown" {
			highlighted, err := a.highlighter.Markdown(ctx, data.Text)
			if err != nil {
				log.Printf("view(%q): rendering markdown: %v", filename, err)
			} else {
				data.Rendered = highlighted
				tmpl = a.viewRenderedTmpl
			}
		} else if renderAsCode(parsedContentType) {
			highlighted, err := a.highlighter.Code(ctx, filename, "Dark", data.Text)
			if err != nil {
				log.Printf("view(%q): rendering markdown: %v", filename, err)
			} else {
				data.Rendered = highlighted
				data.ExpandBorder = true
				tmpl = a.viewRenderedTmpl
			}
		}
	}

	if err := tmpl.Execute(rw, data); err != nil {
		log.Printf("view(%q): Execute: %v", filename, err)
	}
}