// backend/handlers_fs.go
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"

	"scm.bstein.dev/bstein/Pegasus/backend/internal"
)

type listEntry struct {
	Name  string `json:"name"`
	Path  string `json:"path"`
	IsDir bool   `json:"is_dir"`
	Size  int64  `json:"size"`
	Mtime int64  `json:"mtime"`
}

func resolveLibraryRoot(um *internal.UserMap, username, requested string) (string, error) {
	allRoots, _ := um.ResolveAll(username)
	reqLib := sanitizeSegment(requested)
	if reqLib != "" && contains(allRoots, reqLib) {
		return reqLib, nil
	}
	return um.Resolve(username)
}

func listHandler(um *internal.UserMap) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		cl, err := internal.CurrentUser(r)
		if err != nil {
			http.Error(w, "unauthorized", http.StatusUnauthorized)
			return
		}

		rootRel, err := resolveLibraryRoot(um, cl.Username, r.URL.Query().Get("lib"))
		if err != nil {
			internal.Logf("list: map missing for %q", cl.Username)
			http.Error(w, "no mapping", http.StatusForbidden)
			return
		}

		q := sanitizeSegment(r.URL.Query().Get("path"))
		rootAbs, _ := internal.SafeJoin(mediaRoot, rootRel)
		dirAbs := rootAbs
		if q != "" {
			if dirAbs, err = internal.SafeJoin(rootAbs, q); err != nil {
				http.Error(w, "forbidden", http.StatusForbidden)
				return
			}
		}

		ents, err := os.ReadDir(dirAbs)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		out := make([]listEntry, 0, len(ents))
		for _, d := range ents {
			info, _ := d.Info()
			var size int64
			if info != nil && !d.IsDir() {
				size = info.Size()
			}
			var mtime int64
			if info != nil {
				mtime = info.ModTime().Unix()
			}
			out = append(out, listEntry{
				Name:  d.Name(),
				Path:  filepath.Join(q, d.Name()),
				IsDir: d.IsDir(),
				Size:  size,
				Mtime: mtime,
			})
		}
		sort.Slice(out, func(i, j int) bool {
			if out[i].IsDir != out[j].IsDir {
				return out[i].IsDir
			}
			return out[i].Name < out[j].Name
		})
		writeJSON(w, out)
	}
}

func renameHandler(um *internal.UserMap, jf jellyfinClient) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		cl, err := internal.CurrentUser(r)
		if err != nil {
			http.Error(w, "unauthorized", http.StatusUnauthorized)
			return
		}
		var p struct {
			Lib  string `json:"lib"`
			From string `json:"from"`
			To   string `json:"to"`
		}
		if err := json.NewDecoder(r.Body).Decode(&p); err != nil {
			http.Error(w, "bad json", http.StatusBadRequest)
			return
		}
		if filepath.Ext(p.To) != "" && !finalNameRe.MatchString(filepath.Base(p.To)) {
			http.Error(w, "new name must match YYYY.MM.DD.Description.ext", http.StatusBadRequest)
			return
		}

		rootRel, err := resolveLibraryRoot(um, cl.Username, p.Lib)
		if err != nil {
			http.Error(w, "forbidden", http.StatusForbidden)
			return
		}

		rootAbs, _ := internal.SafeJoin(mediaRoot, rootRel)
		fromAbs, err := internal.SafeJoin(rootAbs, strings.TrimPrefix(p.From, "/"))
		if err != nil {
			http.Error(w, "forbidden", http.StatusForbidden)
			return
		}
		toAbs, err := internal.SafeJoin(rootAbs, strings.TrimPrefix(p.To, "/"))
		if err != nil {
			http.Error(w, "forbidden", http.StatusForbidden)
			return
		}

		_ = os.MkdirAll(filepath.Dir(toAbs), 0o2775)
		if internal.DryRun {
			internal.Logf("[DRY] mv %s -> %s", fromAbs, toAbs)
		} else if err := os.Rename(fromAbs, toAbs); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		jf.RefreshLibrary(cl.JFToken)
		writeJSON(w, map[string]any{"ok": true})
	}
}

func deleteHandler(um *internal.UserMap, jf jellyfinClient) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		cl, err := internal.CurrentUser(r)
		if err != nil {
			http.Error(w, "unauthorized", http.StatusUnauthorized)
			return
		}

		rootRel, err := resolveLibraryRoot(um, cl.Username, r.URL.Query().Get("lib"))
		if err != nil {
			http.Error(w, "forbidden", http.StatusForbidden)
			return
		}

		path := strings.TrimPrefix(r.URL.Query().Get("path"), "/")
		rec := r.URL.Query().Get("recursive") == "true"

		rootAbs, _ := internal.SafeJoin(mediaRoot, rootRel)
		abs, err := internal.SafeJoin(rootAbs, path)
		if err != nil {
			http.Error(w, "forbidden", http.StatusForbidden)
			return
		}
		if internal.DryRun {
			internal.Logf("[DRY] rm%s %s", map[bool]string{true: " -r", false: ""}[rec], abs)
		} else if rec {
			err = os.RemoveAll(abs)
		} else {
			err = os.Remove(abs)
		}
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		jf.RefreshLibrary(cl.JFToken)
		writeJSON(w, map[string]any{"ok": true})
	}
}

func mkdirHandler(um *internal.UserMap, jf jellyfinClient) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		cl, err := internal.CurrentUser(r)
		if err != nil {
			http.Error(w, "unauthorized", http.StatusUnauthorized)
			return
		}

		var p struct {
			Lib  string `json:"lib"`
			Path string `json:"path"`
		}
		if err := json.NewDecoder(r.Body).Decode(&p); err != nil {
			http.Error(w, "bad json", http.StatusBadRequest)
			return
		}

		rootRel, err := resolveLibraryRoot(um, cl.Username, p.Lib)
		if err != nil {
			http.Error(w, "forbidden", http.StatusForbidden)
			return
		}

		rootAbs, _ := internal.SafeJoin(mediaRoot, rootRel)
		if fi, err := os.Stat(rootAbs); err != nil || !fi.IsDir() {
			http.Error(w, "library not found", http.StatusBadRequest)
			return
		}

		seg := sanitizeSegment(p.Path)
		if seg == "" {
			http.Error(w, "folder name is empty", http.StatusBadRequest)
			return
		}

		abs, err := internal.SafeJoin(rootAbs, seg)
		if err != nil {
			http.Error(w, "forbidden", http.StatusForbidden)
			return
		}

		if internal.DryRun {
			internal.Logf("[DRY] mkdir -p %s", abs)
		} else if err := os.MkdirAll(abs, 0o2775); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		jf.RefreshLibrary(cl.JFToken)
		writeJSON(w, map[string]any{"ok": true})
	}
}

func debugEnvHandler() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if !internal.Debug {
			http.Error(w, "disabled", http.StatusForbidden)
			return
		}
		writeJSON(w, map[string]any{
			"mediaRoot":   mediaRoot,
			"tusDir":      tusDir,
			"userMapFile": userMapFile,
		})
	}
}

func debugWriteTestHandler(um *internal.UserMap) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if !internal.Debug {
			http.Error(w, "disabled", http.StatusForbidden)
			return
		}
		cl, err := internal.CurrentUser(r)
		if err != nil {
			http.Error(w, "unauthorized", http.StatusUnauthorized)
			return
		}
		rootRel, err := um.Resolve(cl.Username)
		if err != nil {
			http.Error(w, "forbidden", http.StatusForbidden)
			return
		}
		rootAbs, _ := internal.SafeJoin(mediaRoot, rootRel)
		test := filepath.Join(rootAbs, fmt.Sprintf("TEST.%d.txt", time.Now().Unix()))
		if internal.DryRun {
			internal.Logf("[DRY] write %s", test)
		} else {
			_ = os.WriteFile(test, []byte("ok\n"), 0o644)
		}
		writeJSON(w, map[string]string{"wrote": test})
	}
}
