import type { TerminalAPI, TerminalLine } from './types';
import { user } from '$lib/config';

export interface CommandContext {
	terminal: TerminalAPI;
	navigate: (path: string, external?: boolean) => void;
	toggleMode: () => void;
	setColorTheme: (name: string) => void;
	getMode: () => string;
	getColorTheme: () => string;
	themeNames: string[];
	user: typeof user;
	skills: Record<string, string[]>;
	navigation: Array<{ name: string; path: string; external?: boolean }>;
}

export interface Command {
	name: string;
	description: string;
	usage?: string;
	aliases?: string[];
	hidden?: boolean;
	run: (args: string[], ctx: CommandContext) => void;
}

const out = (content: string): TerminalLine => ({ type: 'output', content });
const ok = (content: string): TerminalLine => ({ type: 'success', content });
const err = (content: string): TerminalLine => ({ type: 'error', content });
const info = (content: string): TerminalLine => ({ type: 'info', content });
const blank = (): TerminalLine => ({ type: 'blank', content: '' });

interface PageNode {
	name: string;
	path: string;
	children?: PageNode[];
}

const pageTree: PageNode[] = [
	{ name: 'home', path: '/' },
	{ name: 'about', path: '/about' },
	{ name: 'portfolio', path: '/portfolio' },
	{
		name: 'projects',
		path: '/projects',
		children: [
			{ name: 'opensource', path: '/projects/opensource' },
			{ name: 'packages', path: '/projects/packages' },
			{ name: 'hackathons', path: '/projects/hackathons' },
			{ name: 'research', path: '/projects/research' }
		]
	},
	{ name: 'models', path: '/models' },
	{ name: 'components', path: '/components' },
	{ name: 'links', path: '/links' },
	{ name: 'blog', path: '/blog' }
];

const pageRoutes: Record<string, string> = pageTree.reduce((routes, node) => {
	routes[node.name] = node.path;
	node.children?.forEach((child) => {
		routes[child.name] = child.path;
	});
	return routes;
}, {} as Record<string, string>);

function resolvePagePath(target: string): string | undefined {
	if (pageRoutes[target] !== undefined) return pageRoutes[target];

	const segments = target.split('/').filter(Boolean);
	if (segments.length < 2) return undefined;

	let nodes: PageNode[] = pageTree;
	let path: string | undefined;
	for (const segment of segments) {
		const node = nodes.find((candidate) => candidate.name === segment);
		if (!node) return undefined;
		path = node.path;
		nodes = node.children ?? [];
	}
	return path;
}

function cowsay(message: string): string[] {
	const text = message || 'Moo?';
	const border = '-'.repeat(text.length + 2);
	return [
		` ${border}`,
		`< ${text} >`,
		` ${border}`,
		'        \\   ^__^',
		'         \\  (oo)\\_______',
		'            (__)\\       )\\/\\',
		'                ||----w |',
		'                ||     ||'
	];
}

const baseCommands: Command[] = [
	{
		name: 'help',
		aliases: ['h', '?'],
		description: 'List all available commands',
		run: (_args, ctx) => {
			const rows = commands
				.filter((command) => !command.hidden)
				.map((command) => [
					`(&primary,bold)${command.name}(&)` +
						(command.aliases?.length ? ` (&muted)(${command.aliases.join(', ')})(&)` : ''),
					command.description
				]);
			ctx.terminal.write({
				type: 'table',
				content: 'Available Commands',
				tableHeaders: ['Command', 'Description'],
				tableRows: rows,
				style: 'accent'
			} as TerminalLine);
			ctx.terminal.write(info('Tip: try (&accent)neofetch(&), (&accent)cowsay(&), or (&accent)cd projects(&)'));
			ctx.terminal.write(blank());
		}
	},
	{
		name: 'clear',
		aliases: ['cls'],
		description: 'Clear the terminal',
		run: (_args, ctx) => ctx.terminal.clear()
	},
	{
		name: 'whoami',
		description: 'Show a short bio',
		run: (_args, ctx) => {
			ctx.terminal.writeLines([
				out(`(&primary,bold)${ctx.user.displayname}(&) (&muted)— ${ctx.user.title}(&)`),
				out(`(&muted)${ctx.user.bio}(&)`),
				blank()
			]);
		}
	},
	{
		name: 'neofetch',
		aliases: ['banner'],
		description: 'Display system info',
		run: (_args, ctx) => {
			const launched = new Date('2020-01-01T00:00:00Z');
			const days = Math.floor((Date.now() - launched.getTime()) / 86400000);
			const infoLines: TerminalLine[] = [
				out(`(&accent,bold)${ctx.user.username}(&)(&muted)@(&)(&primary,bold)${ctx.user.hostname}(&)`),
				out('(&muted)------------------------------(&)'),
				out(`(&blue,bold)Name(&): ${ctx.user.displayname}`),
				out(`(&blue,bold)Title(&): ${ctx.user.title}`),
				out(`(&blue,bold)Location(&): ${ctx.user.location}`),
				out(`(&blue,bold)Shell(&): tui-terminal`),
				out(`(&blue,bold)Theme(&): ${ctx.getColorTheme()} (${ctx.getMode()})`),
				out(`(&blue,bold)Uptime(&): ${days} days since launch`),
				out(`(&blue,bold)Languages(&): ${ctx.skills.languages.slice(0, 6).join(', ')}`),
				out('(&red)███(&)(&yellow)███(&)(&green)███(&)(&cyan)███(&)(&blue)███(&)(&magenta)███(&)')
			];
			ctx.terminal.writeLines([
				{
					type: 'image',
					content: '',
					image: ctx.user.avatar,
					imageAlt: ctx.user.displayname,
					imageWidth: 325,
					inline: true
				},
				{
					type: 'group',
					content: '',
					inline: true,
					groupDirection: 'column',
					groupGap: '0',
					children: infoLines
				},
				blank()
			]);
		}
	},
	{
		name: 'skills',
		description: 'List skills by category',
		run: (_args, ctx) => {
			const order = ['languages', 'frameworks', 'tools', 'platforms', 'applications', 'databases', 'interests'];
			const lines: TerminalLine[] = [];
			for (const key of order) {
				const values = ctx.skills[key];
				if (!values?.length) continue;
				lines.push(info(`(&blue,bold)${key}(&)`));
				lines.push(out('  ' + values.map((value) => `(&cyan)${value}(&)`).join(' (&muted)•(&) ')));
			}
			lines.push(blank());
			ctx.terminal.writeLines(lines);
		}
	},
	{
		name: 'contact',
		aliases: ['socials'],
		description: 'Show social links',
		run: (_args, ctx) => {
			ctx.terminal.write(info('(&accent,bold)Find me online:(&)'));
			ctx.terminal.writeLines(
				ctx.user.socials.map((social) => ({
					type: 'button',
					content: social.name,
					icon: social.icon,
					href: social.link,
					style: 'primary',
					inline: true
				})) as TerminalLine[]
			);
			ctx.terminal.write(blank());
		}
	},
	{
		name: 'email',
		description: 'Show contact email',
		run: (_args, ctx) => {
			ctx.terminal.write(out(`(&accent)${ctx.user.email}(&)`));
		}
	},
	{
		name: 'cd',
		aliases: ['goto', 'open'],
		description: 'Navigate to a page',
		usage: 'cd <page> (e.g. projects/opensource)',
		run: (args, ctx) => {
			const target = (args[0] || 'home').toLowerCase().replace(/^\/+|\/+$/g, '');
			const navItem = ctx.navigation.find((item) => item.name.toLowerCase() === target);
			if (navItem) {
				ctx.terminal.write(info(`Navigating to (&accent)${target}(&)...`));
				ctx.navigate(navItem.path, navItem.external);
				return;
			}
			const resolvedPath = resolvePagePath(target);
			if (resolvedPath !== undefined) {
				ctx.terminal.write(info(`Navigating to (&accent)${target}(&)...`));
				ctx.navigate(resolvedPath);
				return;
			}
			ctx.terminal.write(err(`No such page: ${target}. Try (&bold)ls(&).`));
		}
	},
	{
		name: 'ls',
		aliases: ['dir'],
		description: 'List the pages you can visit',
		run: (_args, ctx) => {
			const lines: TerminalLine[] = [];

			pageTree.forEach((node) => {
				lines.push(out(`(&blue)drwxr-xr-x(&)  (&primary,bold)${node.name}(&)/`));
				node.children?.forEach((child, i) => {
					const branch = i === node.children!.length - 1 ? '└──' : '├──';
					lines.push(out(`(&muted)      ${branch}(&) (&primary,bold)${child.name}(&)/`));
				});
			});

			ctx.navigation.forEach((item) => {
				if (item.external && pageRoutes[item.name] === undefined) {
					lines.push(out(`(&blue)drwxr-xr-x(&)  (&primary,bold)${item.name}(&)/`));
				}
			});

			ctx.terminal.writeLines(lines);
		}
	},
	{
		name: 'theme',
		description: 'List or set the color theme',
		usage: 'theme [name]',
		run: (args, ctx) => {
			if (!args.length) {
				ctx.terminal.write(out('Themes: ' + ctx.themeNames.map((name) => `(&blue)${name}(&)`).join('   ')));
				ctx.terminal.write(info(`Current: (&accent)${ctx.getColorTheme()}(&)`));
				return;
			}
			const name = args[0].toLowerCase();
			if (ctx.themeNames.includes(name)) {
				ctx.setColorTheme(name);
				ctx.terminal.write(ok(`Theme set to (&accent)${name}(&)`));
			} else {
				ctx.terminal.write(err(`Unknown theme: ${name}. Available: ${ctx.themeNames.join(', ')}`));
			}
		}
	},
	{
		name: 'mode',
		description: 'Toggle or set dark/light mode',
		usage: 'mode [dark|light]',
		run: (args, ctx) => {
			const current = ctx.getMode();
			if (!args.length) {
				ctx.toggleMode();
				ctx.terminal.write(ok(`Switched to ${current === 'dark' ? 'light' : 'dark'} mode`));
				return;
			}
			const requested = args[0].toLowerCase();
			if (requested === 'dark' || requested === 'light') {
				if (requested !== current) ctx.toggleMode();
				ctx.terminal.write(ok(`${requested} mode active`));
			} else {
				ctx.terminal.write(err('Usage: mode [dark|light]'));
			}
		}
	},
	{
		name: 'echo',
		description: 'Print text back',
		usage: 'echo <text>',
		run: (args, ctx) => ctx.terminal.write(out(args.join(' ')))
	},
	{
		name: 'cowsay',
		description: 'Make a cow say something',
		usage: 'cowsay <text>',
		run: (args, ctx) => {
			const lines = cowsay(args.join(' '));
			ctx.terminal.writeLines(lines.map((line) => out(`(&accent)${line}(&)`)));
		}
	},
	{
		name: 'date',
		description: 'Show the current date and time (Eastern)',
		run: (_args, ctx) => {
			const now = new Date();
			const easternString = now.toLocaleString('en-US', {
				timeZone: 'America/New_York',
				weekday: 'long',
				year: 'numeric',
				month: 'long',
				day: 'numeric',
				hour: '2-digit',
				minute: '2-digit',
				second: '2-digit',
				timeZoneName: 'short'
			});

			const easternParts = new Intl.DateTimeFormat('en-US', {
				timeZone: 'America/New_York',
				year: 'numeric',
				month: '2-digit',
				day: '2-digit'
			}).formatToParts(now);
			const part = (type: string) => Number(easternParts.find((p) => p.type === type)?.value);
			const easternDate = new Date(Date.UTC(part('year'), part('month') - 1, part('day')));
			const startOfYear = new Date(Date.UTC(part('year'), 0, 1));
			const dayOfYear = Math.floor((easternDate.getTime() - startOfYear.getTime()) / 86400000) + 1;

			ctx.terminal.writeLines([
				out(easternString),
				info(`(&muted)Day ${dayOfYear} of ${part('year')}(&)`)
			]);
		}
	},
	{
		name: 'sudo',
		hidden: true,
		description: 'Elevated privileges',
		run: (_args, ctx) =>
			ctx.terminal.write(
				err(`(&error)Nice try.(&) ${ctx.user.username} is not in the sudoers file. This incident will be reported.`)
			)
	}
];

const pageCommands: Command[] = Object.entries(pageRoutes).map(([name, path]) => ({
	name,
	description: `Go to the ${name} page`,
	hidden: true,
	run: (_args, ctx) => ctx.navigate(path)
}));

const socialCommands: Command[] = user.socials.map((social) => ({
	name: social.name.toLowerCase(),
	description: `Open ${social.name}`,
	hidden: true,
	run: (_args, ctx) => ctx.navigate(social.link, true)
}));

export const commands: Command[] = [...baseCommands, ...pageCommands, ...socialCommands];

function findCommand(name: string): Command | undefined {
	const lower = name.toLowerCase();
	return commands.find(
		(command) => command.name === lower || command.aliases?.some((alias) => alias.toLowerCase() === lower)
	);
}

export function runCommand(input: string, ctx: CommandContext): void {
	const raw = input.trim();
	ctx.terminal.write({ type: 'command', content: raw });
	if (!raw) return;

	const parts = raw.split(/\s+/);
	const name = parts[0];
	const args = parts.slice(1);
	const command = findCommand(name);

	if (!command) {
		ctx.terminal.write(err(`command not found: (&bold)${name}(&) — type (&accent)help(&) for a list.`));
		return;
	}

	command.run(args, ctx);
}
