package handlers

import (
	"errors"
	"net/http"
	"os"

	"github.com/google/uuid"

	"mgit.msbls.de/m/paliad/internal/services"
)

// t-paliad-177 — Project Timeline / Chart standalone page.
//
// Slice 1 served dist/projects-chart.html unconditionally and relied on
// the client's first API fetch to enforce visibility. That leaked a 200
// for any well-formed UUID a guesser tried (m/paliad#35 Slice 1 edge
// case #2). Slice 2 closes the leak — we resolve the project via
// ProjectService.GetByID *before* serving the shell so an inaccessible
// id returns 404 + the standard notfound chrome.
//
// Design ref: docs/design-project-chart-2026-05-09.md §8.2 + §12.

func handleProjectsChartPage(w http.ResponseWriter, r *http.Request) {
	if !requireDB(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	id, err := uuid.Parse(r.PathValue("id"))
	if err != nil {
		serveChartNotFound(w)
		return
	}
	if _, err := dbSvc.projects.GetByID(r.Context(), uid, id); err != nil {
		// ErrNotVisible + any "not found" surface from the service collapses
		// to the same outward 404 — never tell a guesser whether the id
		// exists, only whether they can see it.
		if errors.Is(err, services.ErrNotVisible) {
			serveChartNotFound(w)
			return
		}
		// Genuine errors (DB hiccup, etc.) — log via writeServiceError but
		// also fall back to 404 page chrome for the user instead of a raw
		// 500 string. The JSON path of writeServiceError handles /api/*
		// only, so we keep its logging side-effect but render the HTML.
		writeServiceError(httpDevNullJSON{}, err)
		serveChartNotFound(w)
		return
	}
	http.ServeFile(w, r, "dist/projects-chart.html")
}

func serveChartNotFound(w http.ResponseWriter) {
	body, err := os.ReadFile("dist/notfound.html")
	if err != nil {
		http.Error(w, "404 page not found", http.StatusNotFound)
		return
	}
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	w.WriteHeader(http.StatusNotFound)
	_, _ = w.Write(body)
}

// httpDevNullJSON is a writer that discards everything writeServiceError
// would have emitted — we only want the log line, not a duplicate body
// before serveChartNotFound writes the real one.
type httpDevNullJSON struct{}

func (httpDevNullJSON) Header() http.Header        { return http.Header{} }
func (httpDevNullJSON) Write(b []byte) (int, error) { return len(b), nil }
func (httpDevNullJSON) WriteHeader(int)             {}
