/**
 * Everything you need for the `ts-for-gir analyze` command is located here
 */

import { existsSync, readFileSync, writeFileSync } from "node:fs";
import { APP_NAME, Logger } from "@ts-for-gir/lib";
import type { ProblemEntry } from "@ts-for-gir/reporter";
import { analyzeOptions } from "../config.ts";
import type { AnalyzeCommandArgs, ReportData } from "../types/index.ts";
import { createBuilder } from "./command-builder.ts";

const command = "analyze [options]";

const description = "Analyze report files generated by ts-for-gir reporter";

const examples: ReadonlyArray<[string, string?]> = [
	[`${APP_NAME} analyze -f ./ts-for-gir-report.json`, "Show summary statistics of the report"],
	[`${APP_NAME} analyze -f ./ts-for-gir-report.json --summary`, "Show only summary statistics"],
	[`${APP_NAME} analyze -f ./ts-for-gir-report.json --severity error critical`, "Show only errors and critical issues"],
	[
		`${APP_NAME} analyze -f ./ts-for-gir-report.json --category type_resolution --detailed`,
		"Show detailed type resolution problems",
	],
	[`${APP_NAME} analyze -f ./ts-for-gir-report.json --namespace GLib --top 5`, "Show top 5 problems in GLib namespace"],
	[
		`${APP_NAME} analyze -f ./ts-for-gir-report.json --type time_t --export ./time_t_issues.json`,
		"Export all time_t related issues",
	],
	[
		`${APP_NAME} analyze -f ./ts-for-gir-report.json --search "Unable to resolve" --format csv`,
		"Search for resolution failures and export as CSV",
	],
];

const builder = createBuilder<AnalyzeCommandArgs>(analyzeOptions, examples);

const parseReportDate = (dateValue: string | Date): Date => {
	return typeof dateValue === "string" ? new Date(dateValue) : dateValue;
};

const loadReportFile = (filePath: string): ReportData => {
	if (!existsSync(filePath)) {
		throw new Error(`Report file not found: ${filePath}`);
	}

	try {
		const content = readFileSync(filePath, "utf-8");
		const report = JSON.parse(content) as ReportData;

		// Convert string dates to Date objects
		report.metadata.generatedAt = parseReportDate(report.metadata.generatedAt);
		report.statistics.startTime = parseReportDate(report.statistics.startTime);

		if (report.statistics.endTime) {
			report.statistics.endTime = parseReportDate(report.statistics.endTime);
		}

		// Convert problem timestamps
		report.problems = report.problems.map((problem) => ({
			...problem,
			timestamp: parseReportDate(problem.timestamp),
		}));

		return report;
	} catch (error) {
		const errorMessage = error instanceof Error ? error.message : "Unknown error";
		throw new Error(`Failed to parse report file: ${errorMessage}`);
	}
};

const filterProblems = (problems: ProblemEntry[], args: AnalyzeCommandArgs): ProblemEntry[] => {
	let filtered = [...problems];

	// Filter by severity
	if (args.severity?.length) {
		filtered = filtered.filter((p) => args.severity?.includes(p.severity));
	}

	// Filter by category
	if (args.category?.length) {
		filtered = filtered.filter((p) => args.category?.includes(p.category));
	}

	// Filter by namespace
	if (args.namespace?.length) {
		filtered = filtered.filter((p) =>
			args.namespace?.some((ns) => p.location?.includes(ns) || p.module?.includes(ns) || p.metadata?.namespace === ns),
		);
	}

	// Filter by type name
	if (args.type?.length) {
		filtered = filtered.filter((p) => Boolean(p.typeName && args.type?.includes(p.typeName)));
	}

	// Search filter
	if (args.search) {
		const searchLower = args.search.toLowerCase();
		filtered = filtered.filter(
			(p) =>
				p.message.toLowerCase().includes(searchLower) ||
				p.details?.toLowerCase().includes(searchLower) ||
				p.typeName?.toLowerCase().includes(searchLower),
		);
	}

	// Time range filters
	if (args.since) {
		const sinceDate = new Date(args.since);
		filtered = filtered.filter((p) => p.timestamp >= sinceDate);
	}

	if (args.until) {
		const untilDate = new Date(args.until);
		filtered = filtered.filter((p) => p.timestamp <= untilDate);
	}

	return filtered;
};

const displaySummary = (report: ReportData, args: AnalyzeCommandArgs): void => {
	const { statistics } = report;

	console.log("📊 Report Summary\n");

	// Basic info
	console.log(`Generated: ${report.metadata.generatedAt}`);
	console.log(`Version: ${report.metadata.version}`);
	console.log(`Total Problems: ${statistics.totalProblems}`);

	if (statistics.durationMs) {
		console.log(`Generation Duration: ${(statistics.durationMs / 1000).toFixed(2)}s`);
	}

	// Problems by severity
	console.log("\n🔴 Problems by Severity:");
	Object.entries(statistics.bySeverity)
		.filter(([, count]) => count > 0)
		.sort(([, a], [, b]) => b - a)
		.forEach(([severity, count]) => {
			console.log(`  ${severity}: ${count}`);
		});

	// Problems by category
	console.log("\n📂 Problems by Category:");
	Object.entries(statistics.byCategory)
		.filter(([, count]) => count > 0)
		.sort(([, a], [, b]) => b - a)
		.forEach(([category, count]) => {
			console.log(`  ${category}: ${count}`);
		});

	// Top problematic namespaces
	if (statistics.typeStatistics.problematicNamespaces.length > 0) {
		console.log("\n🏢 Most Problematic Namespaces:");
		const topCount = args.top ?? 10;
		statistics.typeStatistics.problematicNamespaces.slice(0, topCount).forEach((ns) => {
			console.log(`  ${ns.namespace}: ${ns.problems} problems`);
			if (args.detailed) {
				const typesList = ns.types.slice(0, 5).join(", ");
				const moreTypes = ns.types.length > 5 ? "..." : "";
				console.log(`    Types: ${typesList}${moreTypes}`);
			}
		});
	}

	// Common unresolved types
	if (statistics.typeStatistics.commonUnresolvedTypes.length > 0) {
		console.log("\n🔍 Most Common Unresolved Types:");
		const topCount = args.top ?? 10;
		statistics.typeStatistics.commonUnresolvedTypes.slice(0, topCount).forEach((type) => {
			console.log(`  ${type.type}: ${type.count} occurrences`);
			if (args.detailed) {
				const namespacesList = type.namespaces.slice(0, 3).join(", ");
				const moreNamespaces = type.namespaces.length > 3 ? "..." : "";
				console.log(`    Namespaces: ${namespacesList}${moreNamespaces}`);
			}
		});
	}
};

const displayProblems = (problems: ProblemEntry[], args: AnalyzeCommandArgs): void => {
	if (problems.length === 0) {
		console.log("No problems match the specified filters.");
		return;
	}

	console.log(`\n🔍 Found ${problems.length} matching problems:\n`);

	problems.forEach((problem, index) => {
		const message = `${index + 1}. [${problem.severity.toUpperCase()}] ${problem.message}`;

		console.log(message);

		if (args.detailed) {
			console.log(`   ID: ${problem.id}`);
			console.log(`   Category: ${problem.category}`);
			console.log(`   Module: ${problem.module}`);
			if (problem.typeName) {
				console.log(`   Type: ${problem.typeName}`);
			}
			if (problem.location) {
				console.log(`   Location: ${problem.location}`);
			}
			if (problem.details) {
				console.log(`   Details: ${problem.details}`);
			}
			console.log(`   Timestamp: ${problem.timestamp}`);
			if (problem.metadata && Object.keys(problem.metadata).length > 0) {
				console.log(`   Metadata: ${JSON.stringify(problem.metadata)}`);
			}
		} else if (problem.typeName) {
			const location = problem.location ?? "unknown";
			console.log(`   Type: ${problem.typeName} | Location: ${location}`);
		}

		if (index < problems.length - 1) {
			console.log("");
		}
	});
};

const formatAsTable = (problems: ProblemEntry[]): string => {
	if (problems.length === 0) {
		return "No problems found.";
	}

	const headers = ["Severity", "Category", "Module", "Type", "Message"];
	const rows = problems.map((p) => [
		p.severity,
		p.category,
		p.module ?? "",
		p.typeName ?? "",
		p.message.length > 50 ? `${p.message.substring(0, 47)}...` : p.message,
	]);

	const columnWidths = headers.map((header, i) => Math.max(header.length, ...rows.map((row) => row[i].length)));

	const separator = columnWidths.map((w) => "-".repeat(w)).join(" | ");
	const headerRow = headers.map((h, i) => h.padEnd(columnWidths[i])).join(" | ");
	const dataRows = rows.map((row) => row.map((cell, i) => cell.padEnd(columnWidths[i])).join(" | "));

	return [headerRow, separator, ...dataRows].join("\n");
};

const formatAsCsv = (problems: ProblemEntry[]): string => {
	const headers = ["id", "severity", "category", "module", "typeName", "location", "message", "details", "timestamp"];
	const rows = problems.map((p) => [
		p.id,
		p.severity,
		p.category,
		p.module ?? "",
		p.typeName ?? "",
		p.location ?? "",
		`"${p.message.replace(/"/g, '""')}"`,
		`"${(p.details ?? "").replace(/"/g, '""')}"`,
		p.timestamp.toISOString(),
	]);

	return [headers.join(","), ...rows.map((row) => row.join(","))].join("\n");
};

const exportResults = (problems: ProblemEntry[], filePath: string, format: string, logger: Logger): void => {
	let content: string;

	switch (format) {
		case "json": {
			content = JSON.stringify(problems, null, 2);
			break;
		}
		case "csv": {
			content = formatAsCsv(problems);
			break;
		}
		case "table": {
			content = formatAsTable(problems);
			break;
		}
		default: {
			throw new Error(`Unsupported export format: ${format}`);
		}
	}

	writeFileSync(filePath, content, "utf-8");
	logger.success(`Results exported to: ${filePath}`);
};

const handler = async (args: AnalyzeCommandArgs): Promise<void> => {
	const logger = new Logger(args.verbose ?? false, "AnalyzeCommand");

	try {
		// Load and parse report file
		const report = loadReportFile(args.reportFile);

		if (args.verbose) {
			logger.info(`Loaded report with ${report.problems.length} problems`);
		}

		// Show summary if requested or if no specific filters are applied
		const hasFilters = Boolean(args.severity || args.category || args.namespace || args.type || args.search);

		if (args.summary || !hasFilters) {
			displaySummary(report, args);
		}

		// If summary-only mode, stop here
		if (args.summary) {
			return;
		}

		// Filter problems based on criteria
		const filteredProblems = filterProblems(report.problems, args);

		// Display filtered results
		if (hasFilters || args.detailed) {
			displayProblems(filteredProblems, args);
		}

		// Export results if requested
		if (args.export) {
			const format = args.format ?? "json";
			exportResults(filteredProblems, args.export, format, logger);
		}

		// Show filter summary if filters were applied
		if (hasFilters && !args.summary) {
			console.log(
				`\n📋 Filter Summary: Showing ${filteredProblems.length} of ${report.problems.length} total problems`,
			);
		}
	} catch (error) {
		const errorMessage = error instanceof Error ? error.message : "Unknown error";
		logger.error(`Analysis failed: ${errorMessage}`);
		process.exit(1);
	}
};

export const analyze = {
	command,
	description,
	builder,
	handler,
	examples,
};
