package main

import (
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

// handleSettings persists the listing view/sort/order to the folder's
// .page-settings file. Values are validated against the allowed sets (unknown
// values fall back to defaults). Triggered by POST /{path}?settings.
func (h *handler) handleSettings(w http.ResponseWriter, r *http.Request, urlPath, fsPath string) {
	if err := r.ParseForm(); err != nil {
		http.Error(w, "bad request", http.StatusBadRequest)
		return
	}
	view := validateView(r.FormValue("view"))
	sortKey := validateSort(r.FormValue("sort"))
	order := validateOrder(r.FormValue("order"))

	if err := writePageSettings(fsPath, view, sortKey, order); err != nil {
		http.Error(w, "write failed: "+err.Error(), http.StatusInternalServerError)
		return
	}
	http.Redirect(w, r, urlPath, http.StatusSeeOther)
}

// writePageSettings performs a read-modify-write of <dir>/.page-settings,
// updating the view/sort/order lines while preserving every other line
// (other keys, comments, blank lines, ordering) verbatim. Missing keys are
// appended. The write is atomic (temp file + rename).
func writePageSettings(dir, view, sortKey, order string) error {
	if err := os.MkdirAll(dir, 0755); err != nil {
		return err
	}
	p := filepath.Join(dir, ".page-settings")
	existing, err := os.ReadFile(p)
	if err != nil && !os.IsNotExist(err) {
		return err
	}
	updated := updateSettingsLines(existing, view, sortKey, order)
	return writeFileAtomic(p, updated, 0644)
}

// updateSettingsLines rewrites the view/sort/order lines in existing while
// leaving all other lines untouched. Every occurrence of a known key is
// updated (so the reader's last-wins parse stays consistent); keys absent from
// the file are appended in a stable order. The result always ends in a newline.
func updateSettingsLines(existing []byte, view, sortKey, order string) []byte {
	targets := map[string]string{"view": view, "sort": sortKey, "order": order}
	appendOrder := []string{"view", "sort", "order"}
	seen := map[string]bool{}

	var lines []string
	if len(existing) > 0 {
		s := string(existing)
		s = strings.TrimSuffix(s, "\n")
		lines = strings.Split(s, "\n")
	}

	for i, line := range lines {
		trimmed := strings.TrimSpace(line)
		if trimmed == "" || strings.HasPrefix(trimmed, "#") {
			continue
		}
		eq := strings.IndexByte(line, '=')
		if eq < 0 {
			continue
		}
		key := strings.TrimSpace(line[:eq])
		if val, ok := targets[key]; ok {
			lines[i] = key + " = " + val
			seen[key] = true
		}
	}

	for _, k := range appendOrder {
		if !seen[k] {
			lines = append(lines, k+" = "+targets[k])
		}
	}

	out := strings.Join(lines, "\n")
	if out != "" {
		out += "\n"
	}
	return []byte(out)
}
