package display

import (
	"fmt"
	"hash/fnv"
	"strings"
)

// Hostname-derived theming: each host hashes its name to an accent color,
// so several vitrines open in browser tabs are distinguishable at a glance.
// The theme name also appears next to the site name in the nav,
// because color alone must not carry the meaning
// (colorblind users, and nearby hues look alike anyway).

// theme is one host's visual identity, computed once at startup.
type Theme struct {
	Name string // the seed itself, shown in the nav next to the site name
	CSS  []byte // ":root" accent override (light and dark), appended after the stylesheet by the /-/style.css handler
}

func NewTheme(seed string) Theme {
	light, dark := AccentColors(ThemeHue(seed))
	return NewThemeColors(seed, light, dark)
}

// AccentColors turns a hue into the accent pair this package would wear at it:
// one color per scheme, with the lightness tuning that keeps the accent AA-legible
// as text on each scheme's paper.  Exported so embedders can accept a chosen hue
// (the hash diversifies nicely, but it cannot be argued with) and still mint
// colors with the same guarantees -- colors, not hues, are what themes are made of.
func AccentColors(hue int) (light, dark string) {
	return fmt.Sprintf("hsl(%d 70%% %d%%)", hue, themeLightness(hue)),
		fmt.Sprintf("hsl(%d 70%% %d%%)", hue, themeLightnessDark)
}

// NewThemeColors builds a theme from an explicit accent pair, one color per scheme.
// This is the ground-level constructor: every theme is colors in the end.
// No tuning applies here -- whoever picks exact colors owns their contrast --
// and the values land in CSS verbatim, so validating them is the caller's business.
func NewThemeColors(name, light, dark string) Theme {
	return Theme{
		Name: name,
		CSS: []byte(fmt.Sprintf(
			":root { --accent: %s; }\n"+
				"@media (prefers-color-scheme: dark) { :root { --accent: %s; } }\n",
			light, dark)),
	}
}

// hostLabel extracts the theme seed from a hostname: the first dot-segment,
// so both "langoustine" and "langoustine.exe.xyz" yield "langoustine".
// (The first segment is the machine-distinct one; the tail is shared domain.)
func HostLabel(hostname string) string {
	label, _, _ := strings.Cut(hostname, ".")
	return label
}

// ThemeHue hashes a theme seed to a hue in [0, 360) via FNV-1a.
// Deterministic and pinned by tests:
// changing this function silently repaints every deployed host.
// Exported so embedders composing extra accent-colored artifacts (favicons, badges)
// can stay on the same wheel without duplicating the hash.
func ThemeHue(seed string) int {
	h := fnv.New32a()
	h.Write([]byte(seed))
	return int(h.Sum32() % 360)
}

// themeLightness picks the accent lightness (percent) for a hue.
// The accent is used for text (h1, links) on the near-white --paper background,
// and at 70% saturation a fixed 35% lightness fails WCAG AA (4.5:1)
// for the whole yellow-green-cyan band: measured, hues 41-191 fall short,
// bottoming out around 3.0:1 at hue 60.
// Dropping that band (with a little margin) to 26% lightness
// brings the worst case over the whole wheel up to ~5.06:1 (at hue 34, just outside the band);
// all hues outside the band clear 4.5:1 at 35%.
func themeLightness(hue int) int {
	if hue >= 35 && hue <= 200 {
		return 26
	}
	return 35
}

// themeLightnessDark is the accent lightness (percent) used against the dark
// --paper background (see the dark-mode block in style.css).
// Unlike the light background, a single flat lightness clears WCAG AA across
// the whole wheel with margin: measured worst case ~5.38:1 at hue 240,
// so there is no yellow/green/cyan band to special-case here.
const themeLightnessDark = 70
