// Package ui contains the user info dashboard ui.
package ui

import (
	"bytes"
	"encoding/json"
	"io"
	"mime"
	"net/http"
	"net/url"
	"path"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"github.com/pomerium/csrf"
	"github.com/pomerium/pomerium/internal/urlutil"
)

// ServeFile serves a file.
func ServeFile(w http.ResponseWriter, r *http.Request, filePath string) error {
	f, etag, err := openFile(filepath.Join("dist", filePath))
	if err != nil {
		return err
	}
	defer f.Close()

	w.Header().Set("ETag", `"`+etag+`"`)
	http.ServeContent(w, r, filepath.Base(filePath), time.Time{}, f.(io.ReadSeeker))
	return nil
}

// ServePage serves the index.html page.
func ServePage(w http.ResponseWriter, r *http.Request, page string, data map[string]interface{}) error {
	if data == nil {
		data = make(map[string]interface{})
	}
	data["csrfToken"] = csrf.Token(r)
	data["page"] = page

	jsonData, err := json.Marshal(data)
	if err != nil {
		return err
	}

	f, _, err := openFile("dist/index.html")
	if err != nil {
		return err
	}
	bs, err := io.ReadAll(f)
	_ = f.Close()
	if err != nil {
		return err
	}

	re, err := regexp.Compile(`(src|href)="(.*?)"`)
	if err != nil {
		return err
	}

	bs = re.ReplaceAllFunc(bs, func(b []byte) []byte {
		parts := re.FindStringSubmatch(string(b))
		if len(parts) < 3 {
			return b
		}
		return []byte(parts[1] + `="` + embedRelativeFileURL(parts[2]) + `"`)
	})

	bs = bytes.Replace(bs,
		[]byte("window.POMERIUM_DATA = {}"),
		append([]byte("window.POMERIUM_DATA = "), jsonData...),
		1)

	http.ServeContent(w, r, "index.html", time.Now(), bytes.NewReader(bs))
	return nil
}

var startTime = time.Now()

func embedRelativeFileURL(rawURL string) (dataURLOrOriginalURL string) {
	u, err := url.Parse(rawURL)
	if err != nil {
		return rawURL
	}

	filePath := strings.Replace(u.Path, "/.pomerium/", "dist/", 1)
	f, _, err := openFile(filePath)
	if err != nil {
		return rawURL
	}
	bs, err := io.ReadAll(f)
	_ = f.Close()
	if err != nil {
		return rawURL
	}

	mediaType := mime.TypeByExtension(path.Ext(rawURL))
	if mediaType == "" {
		mediaType = "application/octet-stream"
	}
	if idx := strings.Index(mediaType, ";"); idx >= 0 {
		mediaType = mediaType[:idx]
	}
	return urlutil.DataURL(mediaType, bs)
}