// Copyright (C) 2022 Marius Schellenberger

package srv

import (
	"bytes"
	"encoding/xml"
	"html/template"
	"net/http"
	"strings"

	"cachefs/pkg/fs"

	"golang.org/x/exp/slices"
)

type statusInterceptor struct {
	w      http.ResponseWriter
	status int
}

func (s *statusInterceptor) Header() http.Header {
	return s.w.Header()
}

func (s *statusInterceptor) Write(p []byte) (int, error) {
	return s.w.Write(p)
}

func (s *statusInterceptor) WriteHeader(statusCode int) {
	s.w.WriteHeader(statusCode)
	s.status = statusCode
}

func (s *statusInterceptor) Status() int {
	if s.status == 0 {
		return http.StatusOK
	}
	return s.status
}

type dirContents struct {
	AllPaths []string `xml:"a"`
	Base     string   `xml:"-"`
	Dirs     dirs     `xml:"-"`
	Files    files    `xml:"-"`
}

type dir struct {
	Name template.HTML
	URI  template.HTML
}

type dirs []dir

func (dirs) Less(i, j dir) bool { return i.Name < j.Name }

type file struct {
	Name    template.HTML
	URI     template.HTML
	Anchor  string
	Status  int
	Prio    int
	Running bool
}

type files []file

func (files) Less(i, j file) bool { return i.Name < j.Name }

type responseInterceptor struct {
	buf    bytes.Buffer
	w      http.ResponseWriter
	status int
}

func (r *responseInterceptor) Header() http.Header {
	return r.w.Header()
}

func (r *responseInterceptor) Write(p []byte) (int, error) {
	return r.buf.Write(p)
}

func (r *responseInterceptor) WriteHeader(statusCode int) {
	r.status = statusCode
}

func (r *responseInterceptor) Status() int {
	if r.status == 0 {
		return http.StatusOK
	}
	return r.status
}

func (r *responseInterceptor) GetPaths(path string, fs *fs.FS, relative bool) (dc dirContents, err error) {
	buf := r.buf.Bytes()
	buf = bytes.ReplaceAll(buf, []byte{'&'}, []byte("&#38;"))
	err = xml.Unmarshal(buf, &dc)
	if err != nil {
		return
	}
	dc.Base = path
	path = strings.TrimSuffix(path, "/")
	for _, p := range dc.AllPaths {
		p = strings.ReplaceAll(p, "&amp;", "&")
		p = strings.ReplaceAll(p, "&#39;", "'")
		name := template.HTML(p)
		full := path + "/" + p
		uri := template.HTML(full)
		if relative {
			uri = template.HTML(p)
		}
		if p[len(p)-1] == '/' {
			dc.Dirs = append(dc.Dirs, dir{
				Name: name,
				URI:  uri,
			})
		} else {
			dc.Files = append(dc.Files, file{
				Name:   name,
				URI:    uri,
				Anchor: anchor(p),
				Status: fs.CacheStatus(full),
			})
		}
	}
	slices.SortFunc(dc.Dirs, dc.Dirs.Less)
	slices.SortFunc(dc.Files, dc.Files.Less)
	return
}

func getPreloads(path string, fs *fs.FS) (dc dirContents) {
	dc.Base = path
	for _, p := range fs.Preloads() {
		dc.Files = append(dc.Files, file{
			Name:    template.HTML(p.Name),
			URI:     template.HTML(p.Name),
			Status:  p.Status,
			Prio:    p.Prio,
			Running: p.Running,
		})
	}
	return
}

func anchor(s string) string {
	if i := strings.LastIndex(s, "/"); i > 0 && i+1 < len(s) {
		s = s[i+1:]
	}
	s = strings.ReplaceAll(s, " ", "_")
	return strings.ToLower(s)
}
