/**
 * Sync service for rNotes — handles re-fetching, conflict detection,
 * and merging for imported notes.
 *
 * Conflict policy:
 * - Remote-only-changed → auto-update
 * - Local-only-changed → keep local
 * - Both changed → mark conflict (stores remote version in conflictContent)
 */

import type { NoteItem, SourceRef } from '../schemas';
import { getConverter, hashContent } from './index';
import { tiptapToMarkdown } from './markdown-tiptap';

export interface SyncResult {
	action: 'unchanged' | 'updated' | 'conflict' | 'error';
	remoteHash?: string;
	error?: string;
	updatedContent?: string;       // TipTap JSON of remote content
	updatedPlain?: string;
	updatedMarkdown?: string;
}

/** Sync a single Notion note by re-fetching from API. */
export async function syncNotionNote(note: NoteItem, token: string): Promise<SyncResult> {
	if (!note.sourceRef || note.sourceRef.source !== 'notion') {
		return { action: 'error', error: 'Note is not from Notion' };
	}

	try {
		const converter = getConverter('notion');
		if (!converter) return { action: 'error', error: 'Notion converter not available' };

		const result = await converter.import({
			pageIds: [note.sourceRef.externalId],
			accessToken: token,
		});

		if (result.notes.length === 0) {
			return { action: 'error', error: 'Could not fetch page from Notion' };
		}

		const remote = result.notes[0];
		const remoteHash = remote.sourceRef.contentHash || '';
		const localHash = note.sourceRef.contentHash || '';

		// Compare hashes
		if (remoteHash === localHash) {
			return { action: 'unchanged' };
		}

		// Check if local was modified since last sync
		const currentLocalHash = hashContent(
			note.contentFormat === 'tiptap-json' ? tiptapToMarkdown(note.content) : note.content
		);
		const localModified = currentLocalHash !== localHash;

		if (!localModified) {
			// Only remote changed — auto-update
			return {
				action: 'updated',
				remoteHash,
				updatedContent: remote.content,
				updatedPlain: remote.contentPlain,
				updatedMarkdown: remote.markdown,
			};
		}

		// Both changed — conflict
		return {
			action: 'conflict',
			remoteHash,
			updatedContent: remote.content,
			updatedPlain: remote.contentPlain,
			updatedMarkdown: remote.markdown,
		};
	} catch (err) {
		return { action: 'error', error: (err as Error).message };
	}
}

/** Sync a single Google Docs note by re-fetching from API. */
export async function syncGoogleDocsNote(note: NoteItem, token: string): Promise<SyncResult> {
	if (!note.sourceRef || note.sourceRef.source !== 'google-docs') {
		return { action: 'error', error: 'Note is not from Google Docs' };
	}

	try {
		const converter = getConverter('google-docs');
		if (!converter) return { action: 'error', error: 'Google Docs converter not available' };

		const result = await converter.import({
			pageIds: [note.sourceRef.externalId],
			accessToken: token,
		});

		if (result.notes.length === 0) {
			return { action: 'error', error: 'Could not fetch doc from Google Docs' };
		}

		const remote = result.notes[0];
		const remoteHash = remote.sourceRef.contentHash || '';
		const localHash = note.sourceRef.contentHash || '';

		if (remoteHash === localHash) {
			return { action: 'unchanged' };
		}

		const currentLocalHash = hashContent(
			note.contentFormat === 'tiptap-json' ? tiptapToMarkdown(note.content) : note.content
		);
		const localModified = currentLocalHash !== localHash;

		if (!localModified) {
			return {
				action: 'updated',
				remoteHash,
				updatedContent: remote.content,
				updatedPlain: remote.contentPlain,
				updatedMarkdown: remote.markdown,
			};
		}

		return {
			action: 'conflict',
			remoteHash,
			updatedContent: remote.content,
			updatedPlain: remote.contentPlain,
			updatedMarkdown: remote.markdown,
		};
	} catch (err) {
		return { action: 'error', error: (err as Error).message };
	}
}

/** Sync file-based notes by re-parsing a ZIP and matching by externalId. */
export async function syncFileBasedNotes(
	notes: NoteItem[],
	zipData: Uint8Array,
	source: 'obsidian' | 'logseq',
): Promise<Map<string, SyncResult>> {
	const results = new Map<string, SyncResult>();

	try {
		const converter = getConverter(source);
		if (!converter) {
			for (const n of notes) results.set(n.id, { action: 'error', error: `${source} converter not available` });
			return results;
		}

		const importResult = await converter.import({ fileData: zipData });
		const remoteMap = new Map<string, typeof importResult.notes[0]>();
		for (const rn of importResult.notes) {
			remoteMap.set(rn.sourceRef.externalId, rn);
		}

		for (const note of notes) {
			if (!note.sourceRef) {
				results.set(note.id, { action: 'error', error: 'No sourceRef' });
				continue;
			}

			const remote = remoteMap.get(note.sourceRef.externalId);
			if (!remote) {
				results.set(note.id, { action: 'unchanged' }); // Not found in ZIP — keep as-is
				continue;
			}

			const remoteHash = remote.sourceRef.contentHash || '';
			const localHash = note.sourceRef.contentHash || '';

			if (remoteHash === localHash) {
				results.set(note.id, { action: 'unchanged' });
				continue;
			}

			const currentLocalHash = hashContent(
				note.contentFormat === 'tiptap-json' ? tiptapToMarkdown(note.content) : note.content
			);
			const localModified = currentLocalHash !== localHash;

			if (!localModified) {
				results.set(note.id, {
					action: 'updated',
					remoteHash,
					updatedContent: remote.content,
					updatedPlain: remote.contentPlain,
					updatedMarkdown: remote.markdown,
				});
			} else {
				results.set(note.id, {
					action: 'conflict',
					remoteHash,
					updatedContent: remote.content,
					updatedPlain: remote.contentPlain,
					updatedMarkdown: remote.markdown,
				});
			}
		}
	} catch (err) {
		for (const n of notes) {
			results.set(n.id, { action: 'error', error: (err as Error).message });
		}
	}

	return results;
}
