/**
 * Google Docs ↔ rNotes converter.
 *
 * Import: Google Docs API structural JSON → markdown → TipTap JSON
 * Export: TipTap JSON → Google Docs batch update requests
 */

import { markdownToTiptap, tiptapToMarkdown, extractPlainTextFromTiptap } from './markdown-tiptap';
import { registerConverter, hashContent } from './index';
import type { ConvertedNote, ImportInput, ImportResult, ExportOptions, ExportResult, NoteConverter } from './index';
import type { NoteItem } from '../schemas';

const DOCS_API_BASE = 'https://docs.googleapis.com/v1';
const DRIVE_API_BASE = 'https://www.googleapis.com/drive/v3';

/** Fetch from Google APIs with auth. */
async function googleFetch(url: string, token: string, opts: RequestInit = {}): Promise<any> {
	const res = await fetch(url, {
		...opts,
		headers: {
			'Authorization': `Bearer ${token}`,
			'Content-Type': 'application/json',
			...opts.headers,
		},
	});

	if (!res.ok) {
		const body = await res.text();
		throw new Error(`Google API error ${res.status}: ${body}`);
	}
	return res.json();
}

/** Convert Google Docs structural elements to markdown. */
function structuralElementToMarkdown(element: any, inlineObjects?: Record<string, any>): string {
	if (element.paragraph) {
		return paragraphToMarkdown(element.paragraph, inlineObjects);
	}
	if (element.table) {
		return tableToMarkdown(element.table);
	}
	if (element.sectionBreak) {
		return '\n---\n';
	}
	return '';
}

/** Convert a Google Docs paragraph to markdown (with inline image resolution context). */
function paragraphToMarkdown(paragraph: any, inlineObjects?: Record<string, any>): string {
	const style = paragraph.paragraphStyle?.namedStyleType || 'NORMAL_TEXT';
	const elements = paragraph.elements || [];
	let text = '';

	for (const el of elements) {
		if (el.textRun) {
			text += textRunToMarkdown(el.textRun);
		} else if (el.inlineObjectElement) {
			const objectId = el.inlineObjectElement.inlineObjectId;
			const obj = inlineObjects?.[objectId];
			if (obj) {
				const imageProps = obj.inlineObjectProperties?.embeddedObject?.imageProperties;
				const contentUri = imageProps?.contentUri;
				if (contentUri) {
					text += `![image](${contentUri})`;
				} else {
					text += `![image](inline-object-${objectId})`;
				}
			} else {
				text += `![image](inline-object)`;
			}
		}
	}

	// Remove trailing newline that Google Docs adds to every paragraph
	text = text.replace(/\n$/, '');

	// Apply heading styles
	switch (style) {
		case 'HEADING_1': return `# ${text}`;
		case 'HEADING_2': return `## ${text}`;
		case 'HEADING_3': return `### ${text}`;
		case 'HEADING_4': return `#### ${text}`;
		case 'HEADING_5': return `##### ${text}`;
		case 'HEADING_6': return `###### ${text}`;
		default: return text;
	}
}

/** Convert a Google Docs TextRun to markdown with formatting. */
function textRunToMarkdown(textRun: any): string {
	let text = textRun.content || '';
	const style = textRun.textStyle || {};

	// Don't apply formatting to whitespace-only text
	if (!text.trim()) return text;

	if (style.bold) text = `**${text.trim()}** `;
	if (style.italic) text = `*${text.trim()}* `;
	if (style.strikethrough) text = `~~${text.trim()}~~ `;
	if (style.link?.url) text = `[${text.trim()}](${style.link.url})`;

	return text;
}

/** Convert a Google Docs table to markdown. */
function tableToMarkdown(table: any): string {
	const rows = table.tableRows || [];
	if (rows.length === 0) return '';

	const mdRows: string[] = [];
	for (let r = 0; r < rows.length; r++) {
		const cells = rows[r].tableCells || [];
		const cellTexts = cells.map((cell: any) => {
			const content = (cell.content || [])
				.map((el: any) => structuralElementToMarkdown(el))
				.join('')
				.trim();
			return content || ' ';
		});
		mdRows.push(`| ${cellTexts.join(' | ')} |`);

		// Separator after header
		if (r === 0) {
			mdRows.push(`| ${cellTexts.map(() => '---').join(' | ')} |`);
		}
	}

	return mdRows.join('\n');
}

/** Convert TipTap markdown to Google Docs batchUpdate requests. */
function markdownToGoogleDocsRequests(md: string): any[] {
	const requests: any[] = [];
	const lines = md.split('\n');
	let index = 1; // Google Docs indexes start at 1

	for (const line of lines) {
		if (!line && lines.indexOf(line) < lines.length - 1) {
			// Empty line → insert newline
			requests.push({
				insertText: { location: { index }, text: '\n' },
			});
			index += 1;
			continue;
		}

		// Headings
		const headingMatch = line.match(/^(#{1,6})\s+(.+)/);
		if (headingMatch) {
			const level = headingMatch[1].length;
			const text = headingMatch[2] + '\n';
			requests.push({
				insertText: { location: { index }, text },
			});
			requests.push({
				updateParagraphStyle: {
					range: { startIndex: index, endIndex: index + text.length },
					paragraphStyle: { namedStyleType: `HEADING_${level}` },
					fields: 'namedStyleType',
				},
			});
			index += text.length;
			continue;
		}

		// Regular text
		const text = line.replace(/^[-*]\s+/, '').replace(/^\d+\.\s+/, '') + '\n';
		requests.push({
			insertText: { location: { index }, text },
		});

		// Apply bullet/list styles
		if (line.match(/^[-*]\s+/)) {
			requests.push({
				createParagraphBullets: {
					range: { startIndex: index, endIndex: index + text.length },
					bulletPreset: 'BULLET_DISC_CIRCLE_SQUARE',
				},
			});
		} else if (line.match(/^\d+\.\s+/)) {
			requests.push({
				createParagraphBullets: {
					range: { startIndex: index, endIndex: index + text.length },
					bulletPreset: 'NUMBERED_DECIMAL_ALPHA_ROMAN',
				},
			});
		}

		index += text.length;
	}

	return requests;
}

const googleDocsConverter: NoteConverter = {
	id: 'google-docs',
	name: 'Google Docs',
	requiresAuth: true,

	async import(input: ImportInput): Promise<ImportResult> {
		const token = input.accessToken;
		if (!token) throw new Error('Google Docs import requires an access token. Connect your Google account first.');
		if (!input.pageIds || input.pageIds.length === 0) {
			throw new Error('No Google Docs selected for import');
		}

		const notes: ConvertedNote[] = [];
		const warnings: string[] = [];

		for (const docId of input.pageIds) {
			try {
				// Fetch document
				const doc = await googleFetch(`${DOCS_API_BASE}/documents/${docId}`, token);
				const title = doc.title || 'Untitled';

				// Convert structural elements to markdown, passing inlineObjects for image resolution
				const body = doc.body?.content || [];
				const inlineObjects = doc.inlineObjects || {};
				const mdParts: string[] = [];

				for (const element of body) {
					const md = structuralElementToMarkdown(element, inlineObjects);
					if (md) mdParts.push(md);
				}

				const markdown = mdParts.join('\n\n');
				const tiptapJson = markdownToTiptap(markdown);
				const contentPlain = extractPlainTextFromTiptap(tiptapJson);

				// Download inline images as attachments
				const attachments: { filename: string; data: Uint8Array; mimeType: string }[] = [];
				for (const [objectId, obj] of Object.entries(inlineObjects) as [string, any][]) {
					const imageProps = obj.inlineObjectProperties?.embeddedObject?.imageProperties;
					const contentUri = imageProps?.contentUri;
					if (contentUri) {
						try {
							const res = await fetch(contentUri, {
								headers: { 'Authorization': `Bearer ${token}` },
							});
							if (res.ok) {
								const data = new Uint8Array(await res.arrayBuffer());
								const ct = res.headers.get('content-type') || 'image/png';
								const ext = ct.includes('jpeg') || ct.includes('jpg') ? 'jpg' : ct.includes('gif') ? 'gif' : ct.includes('webp') ? 'webp' : 'png';
								attachments.push({ filename: `gdocs-${objectId}.${ext}`, data, mimeType: ct });
							}
						} catch { /* skip failed image downloads */ }
					}
				}

				notes.push({
					title,
					content: tiptapJson,
					contentPlain,
					markdown,
					tags: [],
					attachments: attachments.length > 0 ? attachments : undefined,
					sourceRef: {
						source: 'google-docs',
						externalId: docId,
						lastSyncedAt: Date.now(),
						contentHash: hashContent(markdown),
					},
				});
			} catch (err) {
				warnings.push(`Failed to import doc ${docId}: ${(err as Error).message}`);
			}
		}

		return { notes, notebookTitle: 'Google Docs Import', warnings };
	},

	async export(notes: NoteItem[], opts: ExportOptions): Promise<ExportResult> {
		const token = opts.accessToken;
		if (!token) throw new Error('Google Docs export requires an access token. Connect your Google account first.');

		const warnings: string[] = [];
		const results: any[] = [];

		for (const note of notes) {
			try {
				// Create a new Google Doc
				const doc = await googleFetch(`${DOCS_API_BASE}/documents`, token, {
					method: 'POST',
					body: JSON.stringify({ title: note.title }),
				});

				// Convert to markdown
				let md: string;
				if (note.contentFormat === 'tiptap-json' && note.content) {
					md = tiptapToMarkdown(note.content);
				} else {
					md = note.content?.replace(/<[^>]*>/g, '').trim() || '';
				}

				// Build batch update requests
				const requests = markdownToGoogleDocsRequests(md);

				if (requests.length > 0) {
					await googleFetch(`${DOCS_API_BASE}/documents/${doc.documentId}:batchUpdate`, token, {
						method: 'POST',
						body: JSON.stringify({ requests }),
					});
				}

				// Move to folder if parentId specified
				if (opts.parentId) {
					await googleFetch(
						`${DRIVE_API_BASE}/files/${doc.documentId}?addParents=${opts.parentId}`,
						token,
						{ method: 'PATCH', body: JSON.stringify({}) }
					);
				}

				results.push({ noteId: note.id, googleDocId: doc.documentId });
			} catch (err) {
				warnings.push(`Failed to export "${note.title}": ${(err as Error).message}`);
			}
		}

		const data = new TextEncoder().encode(JSON.stringify({ exported: results, warnings }));
		return {
			data,
			filename: 'google-docs-export-results.json',
			mimeType: 'application/json',
		};
	},
};

registerConverter(googleDocsConverter);
