import matter from "gray-matter";
import type {
	AcceptanceCriterion,
	Decision,
	Document,
	ParsedMarkdown,
	RtimeRef,
	StatusHistoryEntry,
	Task,
	TimeEntry,
} from "../types/index.ts";
import { AcceptanceCriteriaManager, extractStructuredSection, STRUCTURED_SECTION_KEYS } from "./structured-sections.ts";

function preprocessFrontmatter(frontmatter: string): string {
	return frontmatter
		.split(/\r?\n/) // Handle both Windows (\r\n) and Unix (\n) line endings
		.map((line) => {
			// Handle both assignee and reporter fields that start with @
			const match = line.match(/^(\s*(?:assignee|reporter):\s*)(.*)$/);
			if (!match) return line;

			const [, prefix, raw] = match;
			const value = raw?.trim() || "";

			if (
				value &&
				!value.startsWith("[") &&
				!value.startsWith("'") &&
				!value.startsWith('"') &&
				!value.startsWith("-")
			) {
				return `${prefix}"${value.replace(/"/g, '\\"')}"`;
			}
			return line;
		})
		.join("\n"); // Always join with \n for consistent YAML parsing
}

function normalizeDate(value: unknown): string {
	if (!value) return "";
	if (value instanceof Date) {
		// Check if this Date object came from a date-only string (time is midnight UTC)
		const hours = value.getUTCHours();
		const minutes = value.getUTCMinutes();
		const seconds = value.getUTCSeconds();

		if (hours === 0 && minutes === 0 && seconds === 0) {
			// This was likely a date-only value, preserve it as date-only
			return value.toISOString().slice(0, 10);
		}
		// This has actual time information, preserve it
		return value.toISOString().slice(0, 16).replace("T", " ");
	}
	const str = String(value)
		.trim()
		.replace(/^['"]|['"]$/g, "");
	if (!str) return "";

	// Check for datetime format first (YYYY-MM-DD HH:mm)
	let match: RegExpMatchArray | null = str.match(/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2})$/);
	if (match) {
		// Already in correct format, return as-is
		return str;
	}

	// Check for ISO datetime format (YYYY-MM-DDTHH:mm)
	match = str.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})$/);
	if (match) {
		// Convert T separator to space
		return str.replace("T", " ");
	}

	// Check for date-only format (YYYY-MM-DD) - backward compatibility
	match = str.match(/^(\d{4})-(\d{2})-(\d{2})$/);
	if (match) {
		return `${match[1]}-${match[2]}-${match[3]}`;
	}

	// Legacy date formats (date-only for backward compatibility)
	match = str.match(/^(\d{2})-(\d{2})-(\d{2})$/);
	if (match) {
		const [day, month, year] = match.slice(1);
		return `20${year}-${month}-${day}`;
	}
	match = str.match(/^(\d{2})\/(\d{2})\/(\d{2})$/);
	if (match) {
		const [day, month, year] = match.slice(1);
		return `20${year}-${month}-${day}`;
	}
	match = str.match(/^(\d{2})\.(\d{2})\.(\d{2})$/);
	if (match) {
		const [day, month, year] = match.slice(1);
		return `20${year}-${month}-${day}`;
	}
	return str;
}

function parseStatusHistory(value: unknown): StatusHistoryEntry[] | undefined {
	if (!value || !Array.isArray(value)) return undefined;
	const entries: StatusHistoryEntry[] = [];
	for (const item of value) {
		if (item && typeof item === "object" && "status" in item && "timestamp" in item) {
			entries.push({
				status: String(item.status),
				timestamp: normalizeDate(item.timestamp),
			});
		}
	}
	return entries.length > 0 ? entries : undefined;
}

function parseTimeEntries(value: unknown): TimeEntry[] | undefined {
	if (!value || !Array.isArray(value)) return undefined;
	const entries: TimeEntry[] = [];
	for (const item of value) {
		if (item && typeof item === "object" && "date" in item && "hours" in item) {
			const entry: TimeEntry = {
				date: String(item.date).slice(0, 10), // ensure YYYY-MM-DD
				hours: Number(item.hours),
			};
			if ("skill" in item && item.skill) entry.skill = String(item.skill);
			if ("note" in item && item.note) entry.note = String(item.note);
			if ("rtimeCommitmentId" in item && item.rtimeCommitmentId) {
				entry.rtimeCommitmentId = String(item.rtimeCommitmentId);
			}
			if ("syncedAt" in item && item.syncedAt) entry.syncedAt = String(item.syncedAt);
			entries.push(entry);
		}
	}
	return entries.length > 0 ? entries : undefined;
}

function parseRtimeRef(value: unknown): RtimeRef | undefined {
	if (!value || typeof value !== "object") return undefined;
	const ref: RtimeRef = {};
	const obj = value as Record<string, unknown>;
	if (obj.taskId) ref.taskId = String(obj.taskId);
	if (obj.space) ref.space = String(obj.space);
	if (Array.isArray(obj.commitmentIds)) {
		ref.commitmentIds = obj.commitmentIds.map(String);
	}
	return ref.taskId || ref.space || ref.commitmentIds ? ref : undefined;
}

export function parseMarkdown(content: string): ParsedMarkdown {
	// Updated regex to handle both Windows (\r\n) and Unix (\n) line endings
	const fmRegex = /^---\r?\n([\s\S]*?)\r?\n---/;
	const match = content.match(fmRegex);
	let toParse = content;

	if (match) {
		const processed = preprocessFrontmatter(match[1] || "");
		// Replace with consistent line endings
		toParse = content.replace(fmRegex, `---\n${processed}\n---`);
	}

	const parsed = matter(toParse);
	return {
		frontmatter: parsed.data,
		content: parsed.content.trim(),
	};
}

export function parseTask(content: string): Task {
	const { frontmatter, content: rawContent } = parseMarkdown(content);

	// Validate priority field
	const priority = frontmatter.priority ? String(frontmatter.priority).toLowerCase() : undefined;
	const validPriorities = ["high", "medium", "low"];
	const validatedPriority =
		priority && validPriorities.includes(priority) ? (priority as "high" | "medium" | "low") : undefined;

	// Parse structured acceptance criteria (checked/text/index) from all sections
	const structuredCriteria: AcceptanceCriterion[] = AcceptanceCriteriaManager.parseAllCriteria(rawContent);

	// Parse other sections
	const descriptionSection = extractStructuredSection(rawContent, STRUCTURED_SECTION_KEYS.description) || "";
	const planSection = extractStructuredSection(rawContent, STRUCTURED_SECTION_KEYS.implementationPlan) || undefined;
	const notesSection = extractStructuredSection(rawContent, STRUCTURED_SECTION_KEYS.implementationNotes) || undefined;

	return {
		id: String(frontmatter.id || ""),
		title: String(frontmatter.title || ""),
		status: String(frontmatter.status || ""),
		assignee: Array.isArray(frontmatter.assignee)
			? frontmatter.assignee.map(String)
			: frontmatter.assignee
				? [String(frontmatter.assignee)]
				: [],
		reporter: frontmatter.reporter ? String(frontmatter.reporter) : undefined,
		createdDate: normalizeDate(frontmatter.created_date),
		updatedDate: frontmatter.updated_date ? normalizeDate(frontmatter.updated_date) : undefined,
		labels: Array.isArray(frontmatter.labels) ? frontmatter.labels.map(String) : [],
		milestone: frontmatter.milestone ? String(frontmatter.milestone) : undefined,
		dependencies: Array.isArray(frontmatter.dependencies) ? frontmatter.dependencies.map(String) : [],
		rawContent,
		acceptanceCriteriaItems: structuredCriteria,
		description: descriptionSection,
		implementationPlan: planSection,
		implementationNotes: notesSection,
		parentTaskId: frontmatter.parent_task_id ? String(frontmatter.parent_task_id) : undefined,
		subtasks: Array.isArray(frontmatter.subtasks) ? frontmatter.subtasks.map(String) : undefined,
		priority: validatedPriority,
		ordinal: frontmatter.ordinal !== undefined ? Number(frontmatter.ordinal) : undefined,
		onStatusChange: frontmatter.onStatusChange ? String(frontmatter.onStatusChange) : undefined,
		statusHistory: parseStatusHistory(frontmatter.status_history),
		doToday: frontmatter.do_today === true || frontmatter.doToday === true,
		estimatedHours:
			frontmatter.estimated_hours !== undefined
				? Number(frontmatter.estimated_hours)
				: frontmatter.estimatedHours !== undefined
					? Number(frontmatter.estimatedHours)
					: undefined,
		dueDate: frontmatter.due_date
			? normalizeDate(frontmatter.due_date)
			: frontmatter.dueDate
				? normalizeDate(frontmatter.dueDate)
				: frontmatter.deadline
					? normalizeDate(frontmatter.deadline)
					: undefined,
		timeEntries: parseTimeEntries(frontmatter.time_entries),
		rtime: parseRtimeRef(frontmatter.rtime),
	};
}

export function parseDecision(content: string): Decision {
	const { frontmatter, content: rawContent } = parseMarkdown(content);

	return {
		id: String(frontmatter.id || ""),
		title: String(frontmatter.title || ""),
		date: normalizeDate(frontmatter.date),
		status: String(frontmatter.status || "proposed") as Decision["status"],
		context: extractSection(rawContent, "Context") || "",
		decision: extractSection(rawContent, "Decision") || "",
		consequences: extractSection(rawContent, "Consequences") || "",
		alternatives: extractSection(rawContent, "Alternatives"),
		rawContent, // Raw markdown content without frontmatter
	};
}

export function parseDocument(content: string): Document {
	const { frontmatter, content: rawContent } = parseMarkdown(content);

	return {
		id: String(frontmatter.id || ""),
		title: String(frontmatter.title || ""),
		type: String(frontmatter.type || "other") as Document["type"],
		createdDate: normalizeDate(frontmatter.created_date),
		updatedDate: frontmatter.updated_date ? normalizeDate(frontmatter.updated_date) : undefined,
		rawContent,
		tags: Array.isArray(frontmatter.tags) ? frontmatter.tags.map(String) : undefined,
	};
}

function extractSection(content: string, sectionTitle: string): string | undefined {
	// Normalize to LF for reliable matching across platforms
	const src = content.replace(/\r\n/g, "\n");
	const regex = new RegExp(`## ${sectionTitle}\\s*\\n([\\s\\S]*?)(?=\\n## |$)`, "i");
	const match = src.match(regex);
	return match?.[1]?.trim();
}
