// Package handler — virtualviewhandler.go: GET dispatch for SSR row +
// MDL/RSK rollup row URLs.
//
// These URLs live in the project-level virtual folders (<project>/ssr,
// <project>/mdl, <project>/rsk) and rewrite to canonical files inside
// <project>/archive/<party>/. The bytes returned to the client are
// augmented with a single path-derived field that the canonical file
// doesn't carry:
//
//   - SSR rows get `name: <party>` so the table renderer has a column
//     to sort on and the form edit pre-fills the party name.
//   - MDL / RSK rollup rows get `party: <party>` so the rollup table
//     can show which package each row came from.
//
// Both fields are stripped before write-back (SSR via serveFormCreateSSR
// strip; MDL/RSK rollup writes go through the generic serveFormUpdate,
// where the path-derived `party:` is rejected by `additionalProperties:
// false` in the underlying schema — so the client must strip it on
// submit, which the tables/form JS already does for path-derived
// fields).
//
// Listings: see fs/tree.go.

package handler

import (
	"net/http"
	"os"
	"strconv"

	"codeberg.org/VARASYS/ZDDC/zddc/internal/zddc"
	"gopkg.in/yaml.v3"
)

// ServeVirtualViewRow serves a GET (or HEAD) for one of the virtual
// row URLs. Caller is expected to have already evaluated ACL against
// vv.PartyArchive's chain.
//
// For SSR rows: returns the canonical archive/<party>/ssr.yaml bytes
// with `name: <party>` injected. If no canonical file exists yet,
// returns `name: <party>\n` (an otherwise-empty row) — the SSR view
// shows every party folder whether or not metadata has been written.
//
// For MDL / RSK rollup rows: returns the canonical bytes with
// `party: <party>` injected. If the canonical file doesn't exist
// (shouldn't happen — the listing only surfaces real files) returns
// 404.
func ServeVirtualViewRow(w http.ResponseWriter, r *http.Request, vv zddc.VirtualViewResolution) {
	if !vv.Resolved || !vv.Kind.IsRowKind() {
		http.Error(w, "Internal Server Error", http.StatusInternalServerError)
		return
	}

	raw, err := os.ReadFile(vv.CanonicalAbs)
	if err != nil {
		if !os.IsNotExist(err) {
			http.Error(w, "Internal Server Error", http.StatusInternalServerError)
			return
		}
		// File doesn't exist yet.
		if vv.Kind != zddc.VirtualViewSSRRow {
			http.NotFound(w, r)
			return
		}
		raw = nil
	}

	var data map[string]any
	if len(raw) > 0 {
		if err := yaml.Unmarshal(raw, &data); err != nil {
			http.Error(w, "parse canonical yaml: "+err.Error(), http.StatusInternalServerError)
			return
		}
	}
	if data == nil {
		data = make(map[string]any)
	}
	switch vv.Kind {
	case zddc.VirtualViewSSRRow:
		data["name"] = vv.Party
	case zddc.VirtualViewMDLRow, zddc.VirtualViewRSKRow:
		data["party"] = vv.Party
	}

	out, err := yaml.Marshal(data)
	if err != nil {
		http.Error(w, "marshal virtual row: "+err.Error(), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/yaml; charset=utf-8")
	w.Header().Set("Cache-Control", "no-store")
	w.Header().Set("X-ZDDC-Source", "virtual-view-row")
	w.Header().Set("X-ZDDC-Resolved-Path", vv.CanonicalURL)
	w.Header().Set("Content-Length", strconv.Itoa(len(out)))
	if r.Method == http.MethodHead {
		w.WriteHeader(http.StatusOK)
		return
	}
	_, _ = w.Write(out)
}
