import type { ResolveStylesheetResult, SemanticCssDiagnostic, SemanticNode } from "@reactive-resume/resume/stylesheet";
import type { ResumeData } from "@reactive-resume/schema/resume/data";
import type { StylesheetMode, StylesheetSource } from "@reactive-resume/schema/resume/stylesheet";
import type { Template } from "@reactive-resume/schema/templates";
import type { ResolvedResumePresentation } from "./context";
import { compileStylesheet, resolveStylesheet } from "@reactive-resume/resume/stylesheet";
import { EMPTY_SEMANTIC_CSS_SOURCE } from "@reactive-resume/schema/resume/stylesheet";
import { shouldShowResumeHeader } from "../templates/shared/cover-letter";
import { getTemplatePageSize } from "../templates/shared/page-size";
import { adaptResolvedPdfNode } from "./adapter";
import { buildPdfBaseStyles } from "./base-styles";
import { createBindingInventory } from "./binding-inventory";
import { semanticNodeKeys } from "./node-keys";
import { getTemplateSemanticBindingRegistry } from "./template-manifest";
import { buildSemanticTree } from "./tree";

export type ResolveResumePresentationInput = {
	data: ResumeData;
	template: Template;
	applied?: StylesheetSource;
	mode: StylesheetMode;
};

export type ResolvedResumeRuntime = {
	presentation: ResolvedResumePresentation;
	sourceTree: SemanticNode;
	renderTree: SemanticNode;
	diagnostics: readonly SemanticCssDiagnostic[];
};

const EMPTY_PRESENTATION = Object.freeze({}) satisfies ResolvedResumePresentation;

const mergeAuthoredPageTrees = (data: ResumeData, template: Template): SemanticNode => {
	const pageTrees = data.metadata.layout.pages.map((page, index) =>
		buildSemanticTree({
			data,
			template,
			page,
			pageNumber: index + 1,
			showHeader: shouldShowResumeHeader(data, index),
		}),
	);
	const root = pageTrees[0];

	return {
		key: semanticNodeKeys.resume(),
		kind: "resume",
		attributes: { template },
		roles: [],
		children: pageTrees.flatMap((tree) => tree.children),
		...(root?.id === undefined ? {} : { id: root.id }),
	};
};

const authoredPageDimensions = (data: ResumeData) => {
	const size = getTemplatePageSize(data.metadata.page.format);
	const dimensions =
		size === "LETTER"
			? { width: 612, height: 792 }
			: typeof size === "object"
				? { width: size.width, height: 841.89 }
				: { width: 595.28, height: 841.89 };

	return data.metadata.layout.pages.map((_page, index) => ({
		pageKey: semanticNodeKeys.page(index + 1),
		...dimensions,
	}));
};

const toPresentation = (
	resolved: ResolveStylesheetResult["nodes"],
	base: ReturnType<typeof buildPdfBaseStyles>,
): ResolvedResumePresentation => {
	return Object.freeze(
		Object.fromEntries(
			Object.entries(resolved).map(([nodeKey, node]) => [nodeKey, adaptResolvedPdfNode(node, base[nodeKey])]),
		),
	) as ResolvedResumePresentation;
};

export function resolveStylesheetMode(data: ResumeData): StylesheetMode {
	return data.metadata.stylesheet?.mode ?? "legacy";
}

export function resolveResumeRuntime({
	data,
	template,
	applied,
	mode,
}: ResolveResumePresentationInput): ResolvedResumeRuntime {
	const sourceTree = mergeAuthoredPageTrees(data, template);
	if (mode !== "semantic") {
		return { presentation: EMPTY_PRESENTATION, sourceTree, renderTree: sourceTree, diagnostics: [] };
	}

	const source = applied ??
		data.metadata.stylesheet?.applied ?? {
			languageVersion: 1,
			text: EMPTY_SEMANTIC_CSS_SOURCE,
		};
	const compiled = compileStylesheet(source);
	if (!compiled.program) {
		return { presentation: EMPTY_PRESENTATION, sourceTree, renderTree: sourceTree, diagnostics: compiled.diagnostics };
	}

	const baseStyles = buildPdfBaseStyles({ data, template, tree: sourceTree });
	const inventory = createBindingInventory(sourceTree, getTemplateSemanticBindingRegistry(template));
	const aliases: Record<string, string[]> = {};
	for (const [aliasKey, binding] of Object.entries(inventory.bindings)) {
		if (binding.type !== "alias") continue;
		aliases[binding.canonicalNodeKey] = [...(aliases[binding.canonicalNodeKey] ?? []), aliasKey];
	}
	const resolved = resolveStylesheet(compiled.program, sourceTree, {
		baseStyles,
		baseSettings: {
			picture: data.picture,
			template,
			design: data.metadata.design,
			typography: data.metadata.typography,
			page: data.metadata.page,
			layout: { sidebarWidth: data.metadata.layout.sidebarWidth },
		},
		pages: authoredPageDimensions(data),
		aliases,
	});
	if (resolved.diagnostics.some(({ severity }) => severity === "error")) {
		return {
			presentation: EMPTY_PRESENTATION,
			sourceTree,
			renderTree: sourceTree,
			diagnostics: [...compiled.diagnostics, ...resolved.diagnostics],
		};
	}

	return {
		presentation: toPresentation(resolved.nodes, baseStyles),
		sourceTree,
		renderTree: resolved.renderTree,
		diagnostics: [...compiled.diagnostics, ...resolved.diagnostics],
	};
}

export function resolveResumePresentation(input: ResolveResumePresentationInput): ResolvedResumePresentation {
	return resolveResumeRuntime(input).presentation;
}
