// the css side of pierre theming lives in src/pierre.css

import {
	getSharedHighlighter,
	registerCustomCSSVariableTheme,
	type FileDiffOptions,
	type FileOptions
} from "@pierre/diffs";
import { browser } from "$app/environment";

// pierre's two diff layouts, threaded from the ?diff= url param down to the
// FileDiff instances
export type DiffStyle = NonNullable<FileDiffOptions<undefined>["diffStyle"]>;

// pierre's theming hook is a registered shiki css-variable theme. the token
// colors come from --diffs-token-* properties in pierre.css, they flip
// with the media query. the name is pinned by app.css's @utility or tailwind
// tree-shakes those rules away
export const PIERRE_THEME_NAME = "tangled";

registerCustomCSSVariableTheme(PIERRE_THEME_NAME, {
	// pierre.css defines the real tokens on diffs-container, these are just
	// fallbacks for the generated bindings
	background: "#ffffff",
	foreground: "#4c4f69",
	"token-comment": "#7c7f93",
	"token-string": "#40a02b",
	"token-string-expression": "#40a02b",
	"token-keyword": "#8839ef",
	"token-function": "#1e66f5",
	"token-constant": "#fe640b",
	"token-parameter": "#e64553",
	"token-punctuation": "#7c7f93",
	"token-link": "#1e66f5",
	"token-inserted": "#40a02b",
	"token-deleted": "#d20f39",
	"token-changed": "#df8e1d"
});

// pierre skips highlighting lines past 1e3 chars by default.
// lockfiles however do not care...
export const PIERRE_TOKENIZE_MAX_LINE_LENGTH = 5000;

// "line-info" separators carry pierre's "N unmodified lines" text, bare
// "simple" ones get a "···"
export const PIERRE_SEPARATOR_CSS =
	// flush against our own file header, pierre's is disabled
	":host{--diffs-gap-block:0}" +
	// overflow auto so the bottom scrollbar only exists when a line overflows
	":host{--diffs-overflow-override:auto}" +
	"[data-separator]{block-size:21px;font-size:14px;line-height:21px;" +
	"color:var(--diffs-fg-number-override,#9ca3af)}" +
	// kill pierre's block gap, padding and rounded chip, the separator reads
	// as one full-width row
	'[data-separator="line-info"]{margin-block:0}' +
	'[data-unified] [data-separator="line-info"] [data-separator-wrapper]{padding-inline:0}' +
	'[data-gutter] [data-separator="line-info"] [data-separator-wrapper]{padding-left:0}' +
	// pierre full-widths the unified separator and the split additions bar but
	// not the split deletions one, that one carries the count text and clips in
	// the 44px gutter
	'[data-deletions] [data-gutter] [data-separator="line-info"] [data-separator-wrapper]{width:100cqi}' +
	'[data-separator="line-info"] [data-separator-content]{border-radius:0}' +
	'[data-separator="line-info"] [data-expand-button]{border-radius:0}' +
	'[data-additions] [data-gutter] [data-separator="line-info"] [data-separator-wrapper]{border-radius:0}' +
	"[data-separator-content]{justify-content:center}" +
	'[data-separator="simple"]{display:flex;align-items:center;justify-content:center}' +
	'[data-separator="simple"]::after{content:"···"}';

export const PIERRE_BASE_OPTIONS = {
	theme: PIERRE_THEME_NAME,
	themeType: "system",
	tokenizeMaxLineLength: PIERRE_TOKENIZE_MAX_LINE_LENGTH
} as const;

// shared by the components and the ssr preloads, the prerendered markup is
// only adoptable when both sides build the same options
export const pierreDiffOptions = (style: DiffStyle): FileDiffOptions<undefined> => ({
	...PIERRE_BASE_OPTIONS,
	diffStyle: style,
	diffIndicators: "classic",
	lineDiffType: "word-alt",
	// "line-info" separators size the bar to span the whole diff.
	// "line-info-basic" never gets that, its count text clips in the 44px
	// gutter cell
	hunkSeparators: "line-info",
	disableFileHeader: true,
	overflow: "scroll",
	unsafeCSS: PIERRE_SEPARATOR_CSS
});

export const pierreFileOptions = (wrap: boolean): FileOptions<undefined> => ({
	...PIERRE_BASE_OPTIONS,
	disableFileHeader: true,
	overflow: wrap ? "wrap" : "scroll"
});

// a renderer with a missing language paints plain text first and swaps in
// the highlight, but only once the shared highlighter is up. before that the
// first render waits on the shiki import and paints nothing, so warm it here
if (browser) void getSharedHighlighter({ themes: [PIERRE_THEME_NAME], langs: [] });
