package main

import (
	"bytes"
	"html/template"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"
)

var quickAddTmpl = template.Must(template.ParseFS(assets, "assets/quickadd.html"))

type quickAddData struct {
	To, URL, Title string
}

// handleQuickAdd serves the bookmarklet popup at /quickadd.
func (h *handler) handleQuickAdd(w http.ResponseWriter, r *http.Request) {
	if !h.checkAuth(w, r) {
		return
	}
	if r.Method != http.MethodGet {
		http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
		return
	}
	q := r.URL.Query()
	to := strings.TrimSpace(q.Get("to"))
	if to == "" {
		http.Error(w, "missing to", http.StatusBadRequest)
		return
	}
	if !strings.HasPrefix(to, "/") {
		to = "/" + to
	}
	data := quickAddData{
		To:    to,
		URL:   q.Get("url"),
		Title: q.Get("title"),
	}
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	if err := quickAddTmpl.Execute(w, data); err != nil {
		log.Printf("quickadd template: %v", err)
	}
}

// handleAppend appends one link entry to index.md at fsPath. Creates the
// folder and index.md if missing. Body is form-encoded with `url` (required),
// `title` and `comment` (both optional).
func (h *handler) handleAppend(w http.ResponseWriter, r *http.Request, urlPath, fsPath string) {
	if err := r.ParseForm(); err != nil {
		http.Error(w, "bad request", http.StatusBadRequest)
		return
	}
	rawURL := strings.TrimSpace(r.FormValue("url"))
	title := strings.TrimSpace(r.FormValue("title"))
	comment := strings.TrimSpace(r.FormValue("comment"))
	if rawURL == "" {
		http.Error(w, "missing url", http.StatusBadRequest)
		return
	}
	if title == "" {
		title = rawURL
	}

	entry := formatAppendEntry(title, rawURL, comment, time.Now())

	_, statErr := os.Stat(fsPath)
	newlyCreated := os.IsNotExist(statErr)
	if err := os.MkdirAll(fsPath, 0755); err != nil {
		http.Error(w, "mkdir failed: "+err.Error(), http.StatusInternalServerError)
		return
	}

	indexPath := filepath.Join(fsPath, "index.md")
	existing, err := os.ReadFile(indexPath)
	if err != nil && !os.IsNotExist(err) {
		http.Error(w, "read failed: "+err.Error(), http.StatusInternalServerError)
		return
	}

	var buf bytes.Buffer
	if len(existing) > 0 {
		buf.Write(existing)
		if existing[len(existing)-1] != '\n' {
			buf.WriteByte('\n')
		}
	}
	buf.WriteString(entry)

	if err := os.WriteFile(indexPath, buf.Bytes(), 0644); err != nil {
		http.Error(w, "write failed: "+err.Error(), http.StatusInternalServerError)
		return
	}

	if newlyCreated {
		if rel, err := filepath.Rel(h.root, fsPath); err == nil {
			folderIndexAdd(filepath.ToSlash(rel))
		}
	}
	w.WriteHeader(http.StatusNoContent)
}

// formatAppendEntry builds a CommonMark multi-line list item: a link with a
// continuation-indented timestamp line and an optional comment line. Lines
// after the first share an indent so goldmark folds them into one paragraph.
func formatAppendEntry(title, rawURL, comment string, ts time.Time) string {
	var b strings.Builder
	b.WriteString("- [")
	b.WriteString(escapeLinkLabel(title))
	b.WriteString("](")
	b.WriteString(rawURL)
	b.WriteString(")\n")
	b.WriteString(ts.Format("2006-01-02 15:04"))
	b.WriteString("\n")
	if comment != "" {
		b.WriteString("  ")
		b.WriteString(comment)
		b.WriteByte('\n')
	}
	return b.String()
}

// escapeLinkLabel backslash-escapes the brackets that would otherwise close
// the markdown link label early. The label text is rendered verbatim, so we
// keep all other characters as-is.
func escapeLinkLabel(s string) string {
	s = strings.ReplaceAll(s, `\`, `\\`)
	s = strings.ReplaceAll(s, `[`, `\[`)
	s = strings.ReplaceAll(s, `]`, `\]`)
	return s
}
