import type { ResumeData } from "@reactive-resume/schema/resume/data";
import type { Template } from "@reactive-resume/schema/templates";
import z, { ZodError } from "zod";
import { resumeDataSchema } from "@reactive-resume/schema/resume/data";
import { templateSchema } from "@reactive-resume/schema/templates";
import { parseColorString } from "@reactive-resume/utils/color";
import { generateId } from "@reactive-resume/utils/string";
import { rethrowAsImportError } from "./error";

function colorToRgba(color: string): string {
	const parsed = parseColorString(color);
	if (!parsed) return "rgba(0, 0, 0, 1)";
	return `rgba(${parsed.r}, ${parsed.g}, ${parsed.b}, ${parsed.a})`;
}

// ponytail: inlined single-caller wrappers; compile-time constants (rotation=0, sidebarWidth=35, shadow=0) replaced with literals
const clamp = (value: number, min: number, max: number): number => Math.max(min, Math.min(max, value));

const isValidEmail = (email: string): boolean => {
	if (!email) return false;
	return z.email().safeParse(email).success;
};

const sanitizeEmail = (email: string | undefined): string => {
	if (!email) return "";
	return isValidEmail(email) ? email : "";
};

type FontWeight = "100" | "200" | "300" | "400" | "500" | "600" | "700" | "800" | "900";

const FONT_WEIGHT_MAP: Record<string, FontWeight> = {
	regular: "400",
	italic: "400",
	"100": "100",
	"200": "200",
	"300": "300",
	"400": "400",
	"500": "500",
	"600": "600",
	"700": "700",
	"800": "800",
	"900": "900",
	bold: "700",
	"bold-italic": "700",
};

const convertFontVariantToWeight = (variant: string, defaultWeight: FontWeight = "400"): FontWeight =>
	FONT_WEIGHT_MAP[variant.toLowerCase()] ?? defaultWeight;

const convertFontVariants = (variants: string[] | undefined, defaultWeight: FontWeight = "400"): FontWeight[] => {
	if (!variants || variants.length === 0) return [defaultWeight];
	return variants.map((v) => convertFontVariantToWeight(v, defaultWeight));
};

const convertFontVariantsForHeading = (variants: string[] | undefined): FontWeight[] => {
	const weights = convertFontVariants(variants, "600");
	const filtered = weights.filter((w) => Number.parseInt(w, 10) >= 600);
	return filtered.length > 0 ? filtered : ["600"];
};

// ponytail: V4Url + V4Section<T> collapse 11× {label,href} and 13× 5-field section header into aliases
type V4Url = { label: string; href: string };
type V4Section<T> = {
	name: string;
	columns: number;
	separateLinks: boolean;
	visible: boolean;
	id: string;
	items: Array<T>;
};

type V4ResumeData = {
	basics: {
		name: string;
		headline: string;
		email: string;
		phone: string;
		location: string;
		url: V4Url;
		customFields: Array<{ id?: string; icon?: string; text?: string }>;
		picture: {
			url: string;
			size: number;
			aspectRatio: number;
			borderRadius: number;
			effects: { hidden: boolean; border: boolean; grayscale: boolean };
		};
	};
	sections: {
		summary: { name: string; columns: number; separateLinks: boolean; visible: boolean; id: string; content: string };
		awards: V4Section<{
			id: string;
			visible: boolean;
			title?: string;
			awarder?: string;
			date?: string;
			summary?: string;
			url?: V4Url;
		}>;
		certifications: V4Section<{
			id: string;
			visible: boolean;
			name?: string;
			issuer?: string;
			date?: string;
			summary?: string;
			url?: V4Url;
		}>;
		education: V4Section<{
			id: string;
			visible: boolean;
			institution?: string;
			studyType?: string;
			area?: string;
			score?: string;
			date?: string;
			summary?: string;
			url?: V4Url;
		}>;
		experience: V4Section<{
			id: string;
			visible: boolean;
			company?: string;
			position?: string;
			location?: string;
			date?: string;
			summary?: string;
			url?: V4Url;
		}>;
		volunteer: V4Section<{
			id: string;
			visible: boolean;
			organization?: string;
			position?: string;
			location?: string;
			date?: string;
			summary?: string;
			url?: V4Url;
		}>;
		interests: V4Section<{ id: string; visible: boolean; name?: string; keywords?: string[] }>;
		languages: V4Section<{ id: string; visible: boolean; name?: string; description?: string; level?: number }>;
		profiles: V4Section<{
			id: string;
			visible: boolean;
			network?: string;
			username?: string;
			icon?: string;
			url?: V4Url;
		}>;
		projects: V4Section<{
			id: string;
			visible: boolean;
			name?: string;
			description?: string;
			date?: string;
			summary?: string;
			keywords?: string[];
			url?: V4Url;
		}>;
		publications: V4Section<{
			id: string;
			visible: boolean;
			name?: string;
			publisher?: string;
			date?: string;
			summary?: string;
			url?: V4Url;
		}>;
		references: V4Section<{
			id: string;
			visible: boolean;
			name?: string;
			description?: string;
			summary?: string;
			url?: V4Url;
		}>;
		skills: V4Section<{
			id: string;
			visible: boolean;
			name?: string;
			description?: string;
			level?: number;
			keywords?: string[];
		}>;
		custom?: Record<
			string,
			V4Section<{
				id: string;
				visible: boolean;
				name?: string;
				description?: string;
				date?: string;
				location?: string;
				summary?: string;
				keywords?: string[];
				url?: V4Url;
			}>
		>;
	};
	metadata: {
		template: string;
		layout: Array<Array<string[]>>;
		css: { value: string; visible: boolean };
		page: { margin: number; format: "a4" | "letter"; options: { breakLine: boolean; pageNumbers: boolean } };
		theme: { background: string; text: string; primary: string };
		typography: {
			font: { family: string; subset: string; variants: string[]; size: number };
			lineHeight: number;
			hideIcons: boolean;
			underlineLinks: boolean;
		};
		notes: string;
	};
};

// Transform layout section ID from V4 format to new format
// V4 uses "custom.{id}" for custom sections, new format just uses "{id}"
const transformLayoutSectionId = (id: string): string => {
	if (id.startsWith("custom.")) return id.slice(7);
	return id;
};

// Transform layout column by stripping "custom." prefix from section IDs
const transformLayoutColumn = (column: string[]): string[] => {
	return column
		.filter((id) => id !== "summary") // Summary is handled separately
		.map(transformLayoutSectionId);
};

// ponytail: stateless single-method class → plain function
export function parseReactiveResumeV4JSON(json: string): ResumeData {
	try {
		const v4Data = JSON.parse(json) as V4ResumeData;

		const transformed: ResumeData = {
			picture: {
				hidden: v4Data.basics.picture?.effects?.hidden ?? false,
				url: v4Data.basics.picture?.url ?? "",
				size: clamp(v4Data.basics.picture?.size ?? 80, 32, 512),
				rotation: 0,
				aspectRatio: clamp(v4Data.basics.picture?.aspectRatio ?? 1, 0.5, 2.5),
				borderRadius: clamp(v4Data.basics.picture?.borderRadius ?? 0, 0, 100),
				borderColor: v4Data.basics.picture?.effects?.border ? "rgba(0, 0, 0, 0.5)" : "rgba(0, 0, 0, 0)",
				borderWidth: v4Data.basics.picture?.effects?.border ? 1 : 0,
				shadowColor: "rgba(0, 0, 0, 0.5)",
				shadowWidth: 0,
			},
			basics: {
				name: v4Data.basics.name ?? "",
				headline: v4Data.basics.headline ?? "",
				email: sanitizeEmail(v4Data.basics.email),
				phone: v4Data.basics.phone ?? "",
				location: v4Data.basics.location ?? "",
				website: {
					url: v4Data.basics.url?.href ?? "",
					label: v4Data.basics.url?.label ?? "",
				},
				customFields: (v4Data.basics.customFields ?? []).map((field) => ({
					id: field.id ?? generateId(),
					icon: field.icon ?? "",
					text: field.text ?? "",
					link: "",
				})),
			},
			summary: {
				title: v4Data.sections.summary?.name ?? "",
				icon: "",
				columns: v4Data.sections.summary?.columns ?? 1,
				hidden: !(v4Data.sections.summary?.visible ?? true),
				keepTogether: false,
				startOnNewPage: false,
				content: v4Data.sections.summary?.content ?? "",
			},
			sections: {
				profiles: {
					title: v4Data.sections.profiles?.name ?? "",
					icon: "",
					columns: v4Data.sections.profiles?.columns ?? 1,
					hidden: !(v4Data.sections.profiles?.visible ?? true),
					keepTogether: false,
					startOnNewPage: false,
					items: (v4Data.sections.profiles?.items ?? [])
						.filter((item) => item.network && item.network.length > 0)
						.map((item) => ({
							id: item.id ?? generateId(),
							hidden: !(item.visible ?? true),
							icon: item.icon ?? "",
							iconColor: "",
							network: item.network ?? "",
							username: item.username ?? "",
							website: {
								url: item.url?.href ?? "",
								label: item.url?.label ?? "",
								inlineLink: false,
							},
						})),
				},
				experience: {
					title: v4Data.sections.experience?.name ?? "",
					icon: "",
					columns: v4Data.sections.experience?.columns ?? 1,
					hidden: !(v4Data.sections.experience?.visible ?? true),
					keepTogether: false,
					startOnNewPage: false,
					items: (v4Data.sections.experience?.items ?? [])
						.filter((item) => item.company && item.company.length > 0)
						.map((item) => ({
							id: item.id ?? generateId(),
							hidden: !(item.visible ?? true),
							company: item.company ?? "",
							position: item.position ?? "",
							location: item.location ?? "",
							period: item.date ?? "",
							website: {
								url: item.url?.href ?? "",
								label: item.url?.label ?? "",
								inlineLink: false,
							},
							roles: [],
							description: item.summary ?? "",
						})),
				},
				education: {
					title: v4Data.sections.education?.name ?? "",
					icon: "",
					columns: v4Data.sections.education?.columns ?? 1,
					hidden: !(v4Data.sections.education?.visible ?? true),
					keepTogether: false,
					startOnNewPage: false,
					items: (v4Data.sections.education?.items ?? [])
						.filter((item) => item.institution && item.institution.length > 0)
						.map((item) => ({
							id: item.id ?? generateId(),
							hidden: !(item.visible ?? true),
							school: item.institution ?? "",
							degree: item.studyType ?? "",
							area: item.area ?? "",
							grade: item.score ?? "",
							location: "",
							period: item.date ?? "",
							website: {
								url: item.url?.href ?? "",
								label: item.url?.label ?? "",
								inlineLink: false,
							},
							description: item.summary ?? "",
						})),
				},
				projects: {
					title: v4Data.sections.projects?.name ?? "",
					icon: "",
					columns: v4Data.sections.projects?.columns ?? 1,
					hidden: !(v4Data.sections.projects?.visible ?? true),
					keepTogether: false,
					startOnNewPage: false,
					items: (v4Data.sections.projects?.items ?? [])
						.filter((item) => item.name && item.name.length > 0)
						.map((item) => ({
							id: item.id ?? generateId(),
							hidden: !(item.visible ?? true),
							name: item.name ?? "",
							period: item.date ?? "",
							website: {
								url: item.url?.href ?? "",
								label: item.url?.label ?? "",
								inlineLink: false,
							},
							description: item.summary ?? item.description ?? "",
						})),
				},
				skills: {
					title: v4Data.sections.skills?.name ?? "",
					icon: "",
					columns: v4Data.sections.skills?.columns ?? 1,
					hidden: !(v4Data.sections.skills?.visible ?? true),
					keepTogether: false,
					startOnNewPage: false,
					items: (v4Data.sections.skills?.items ?? [])
						.filter((item) => item.name && item.name.length > 0)
						.map((item) => ({
							id: item.id ?? generateId(),
							hidden: !(item.visible ?? true),
							icon: "",
							iconColor: "",
							name: item.name ?? "",
							proficiency: item.description ?? "",
							// v4 stored skill level as 0-10; scale down to v5's 0-5 range
							level: Math.round(clamp(item.level ?? 0, 0, 10) / 2),
							keywords: item.keywords ?? [],
						})),
				},
				languages: {
					title: v4Data.sections.languages?.name ?? "",
					icon: "",
					columns: v4Data.sections.languages?.columns ?? 1,
					hidden: !(v4Data.sections.languages?.visible ?? true),
					keepTogether: false,
					startOnNewPage: false,
					items: (v4Data.sections.languages?.items ?? [])
						.filter((item) => item.name && item.name.length > 0)
						.map((item) => ({
							id: item.id ?? generateId(),
							hidden: !(item.visible ?? true),
							language: item.name ?? "",
							fluency: item.description ?? "",
							// v4 stored language level as 0-10; scale down to v5's 0-5 range
							level: Math.round(clamp(item.level ?? 0, 0, 10) / 2),
						})),
				},
				interests: {
					title: v4Data.sections.interests?.name ?? "",
					icon: "",
					columns: v4Data.sections.interests?.columns ?? 1,
					hidden: !(v4Data.sections.interests?.visible ?? true),
					keepTogether: false,
					startOnNewPage: false,
					items: (v4Data.sections.interests?.items ?? [])
						.filter((item) => item.name && item.name.length > 0)
						.map((item) => ({
							id: item.id ?? generateId(),
							hidden: !(item.visible ?? true),
							icon: "",
							iconColor: "",
							name: item.name ?? "",
							keywords: item.keywords ?? [],
						})),
				},
				awards: {
					title: v4Data.sections.awards?.name ?? "",
					icon: "",
					columns: v4Data.sections.awards?.columns ?? 1,
					hidden: !(v4Data.sections.awards?.visible ?? true),
					keepTogether: false,
					startOnNewPage: false,
					items: (v4Data.sections.awards?.items ?? [])
						.filter((item) => item.title && item.title.length > 0)
						.map((item) => ({
							id: item.id ?? generateId(),
							hidden: !(item.visible ?? true),
							title: item.title ?? "",
							awarder: item.awarder ?? "",
							date: item.date ?? "",
							website: {
								url: item.url?.href ?? "",
								label: item.url?.label ?? "",
								inlineLink: false,
							},
							description: item.summary ?? "",
						})),
				},
				certifications: {
					title: v4Data.sections.certifications?.name ?? "",
					icon: "",
					columns: v4Data.sections.certifications?.columns ?? 1,
					hidden: !(v4Data.sections.certifications?.visible ?? true),
					keepTogether: false,
					startOnNewPage: false,
					items: (v4Data.sections.certifications?.items ?? [])
						.filter((item) => item.name && item.name.length > 0)
						.map((item) => ({
							id: item.id ?? generateId(),
							hidden: !(item.visible ?? true),
							title: item.name ?? "",
							issuer: item.issuer ?? "",
							date: item.date ?? "",
							website: {
								url: item.url?.href ?? "",
								label: item.url?.label ?? "",
								inlineLink: false,
							},
							description: item.summary ?? "",
						})),
				},
				publications: {
					title: v4Data.sections.publications?.name ?? "",
					icon: "",
					columns: v4Data.sections.publications?.columns ?? 1,
					hidden: !(v4Data.sections.publications?.visible ?? true),
					keepTogether: false,
					startOnNewPage: false,
					items: (v4Data.sections.publications?.items ?? [])
						.filter((item) => item.name && item.name.length > 0)
						.map((item) => ({
							id: item.id ?? generateId(),
							hidden: !(item.visible ?? true),
							title: item.name ?? "",
							publisher: item.publisher ?? "",
							date: item.date ?? "",
							website: {
								url: item.url?.href ?? "",
								label: item.url?.label ?? "",
								inlineLink: false,
							},
							description: item.summary ?? "",
						})),
				},
				volunteer: {
					title: v4Data.sections.volunteer?.name ?? "",
					icon: "",
					columns: v4Data.sections.volunteer?.columns ?? 1,
					hidden: !(v4Data.sections.volunteer?.visible ?? true),
					keepTogether: false,
					startOnNewPage: false,
					items: (v4Data.sections.volunteer?.items ?? [])
						.filter((item) => item.organization && item.organization.length > 0)
						.map((item) => ({
							id: item.id ?? generateId(),
							hidden: !(item.visible ?? true),
							organization: item.organization ?? "",
							location: item.location ?? "",
							period: item.date ?? "",
							website: {
								url: item.url?.href ?? "",
								label: item.url?.label ?? "",
								inlineLink: false,
							},
							description: item.summary ?? "",
						})),
				},
				references: {
					title: v4Data.sections.references?.name ?? "",
					icon: "",
					columns: v4Data.sections.references?.columns ?? 1,
					hidden: !(v4Data.sections.references?.visible ?? true),
					keepTogether: false,
					startOnNewPage: false,
					items: (v4Data.sections.references?.items ?? [])
						.filter((item) => item.name && item.name.length > 0)
						.map((item) => ({
							id: item.id ?? generateId(),
							hidden: !(item.visible ?? true),
							name: item.name ?? "",
							position: item.description ?? "",
							phone: "",
							website: {
								url: item.url?.href ?? "",
								label: item.url?.label ?? "",
								inlineLink: false,
							},
							description: item.summary ?? "",
						})),
				},
			},
			customSections: Object.entries(v4Data.sections.custom ?? {}).map(([sectionId, section]) => ({
				id: section.id || sectionId,
				title: section.name ?? "",
				icon: "",
				type: "experience" as const, // Default to experience type as it has the most compatible fields
				columns: section.columns ?? 1,
				hidden: !(section.visible ?? true),
				keepTogether: false,
				startOnNewPage: false,
				items: section.items.map((item, index) => {
					const hasName = Boolean(item.name?.trim());
					return {
						id: item.id || generateId(),
						hidden: !(item.visible ?? true),
						company: item.name?.trim() || `#${index + 1}`,
						// Only use description as subtitle when item has a name;
						// otherwise description IS the primary content and goes to the body below
						position: hasName ? (item.description ?? "") : "",
						location: item.location ?? "",
						period: item.date ?? "",
						website: {
							url: item.url?.href ?? "",
							label: item.url?.label ?? "",
							inlineLink: false,
						},
						roles: [],
						// Prefer HTML summary; fall back to plain description
						// (for description-only items, description IS the body content)
						description: item.summary ?? item.description ?? "",
					};
				}),
			})),
			metadata: {
				template: (templateSchema.safeParse(v4Data.metadata.template).success
					? v4Data.metadata.template
					: "onyx") as Template,
				layout: {
					sidebarWidth: 35,
					pages: (v4Data.metadata.layout ?? []).map((page) => {
						const main = transformLayoutColumn(page[0] ?? []);
						const sidebar = transformLayoutColumn(page[1] ?? []);
						return {
							fullWidth: sidebar.length === 0,
							main,
							sidebar,
						};
					}),
				},
				page: {
					gapX: 4,
					gapY: 6,
					marginX: Math.max(0, v4Data.metadata.page?.margin ?? 14),
					marginY: Math.max(0, v4Data.metadata.page?.margin ?? 14),
					format: v4Data.metadata.page?.format ?? "a4",
					locale: "en-US",
					hideLinkUnderline: v4Data.metadata.typography?.underlineLinks === false,
					hideIcons: v4Data.metadata.typography?.hideIcons ?? false,
					hideSectionIcons: true,
				},
				design: {
					colors: {
						primary: v4Data.metadata.theme?.primary
							? colorToRgba(v4Data.metadata.theme.primary)
							: "rgba(220, 38, 38, 1)",
						text: v4Data.metadata.theme?.text ? colorToRgba(v4Data.metadata.theme.text) : "rgba(0, 0, 0, 1)",
						background: v4Data.metadata.theme?.background
							? colorToRgba(v4Data.metadata.theme.background)
							: "rgba(255, 255, 255, 1)",
					},
					level: {
						icon: "star",
						type: "circle",
					},
				},
				typography: {
					body: {
						fontFamily: v4Data.metadata.typography?.font?.family ?? "IBM Plex Serif",
						fontWeights: convertFontVariants(v4Data.metadata.typography?.font?.variants),
						fontSize: clamp((v4Data.metadata.typography?.font?.size ?? 14.67) * 0.75, 6, 24),
						lineHeight: clamp(v4Data.metadata.typography?.lineHeight ?? 1.5, 0.5, 4),
					},
					heading: {
						fontFamily: v4Data.metadata.typography?.font?.family ?? "IBM Plex Serif",
						fontWeights: convertFontVariantsForHeading(v4Data.metadata.typography?.font?.variants),
						fontSize: clamp(clamp((v4Data.metadata.typography?.font?.size ?? 14.67) * 0.75, 6, 24) + 3, 6, 24),
						lineHeight: clamp(v4Data.metadata.typography?.lineHeight ?? 1.5, 0.5, 4),
					},
				},
				notes: v4Data.metadata.notes ?? "",
				styleRules: [],
			},
		};

		if (v4Data.sections.summary?.visible && v4Data.sections.summary?.content) {
			const firstPage = transformed.metadata.layout.pages[0];
			if (firstPage) {
				firstPage.main.unshift("summary");
			}
		}

		return resumeDataSchema.parse(transformed);
	} catch (error: unknown) {
		if (error instanceof ZodError) rethrowAsImportError(error);
		throw error;
	}
}
