/**
 * OpenCode client utility
 *
 * Orchestrates prompt building and OpenCode SDK calls.
 * All business logic runs in the browser; SDK calls go through message passing to the extension.
 */

import { formatAllPrompts } from './opencode-comment-extraction';
import { buildPrompt } from './opencode-prompt';
import { createSession, sendPrompt } from '../services/opencode-client';
import { processAnswers } from './answer-processing';
import { documentHtmlToMarkdown, documentMarkdownToHtml } from './html-to-markdown';
import type { AnnotationContent } from './annotation-types';
import { webviewLogger } from '../services/webview-logger';

export interface ApplyCommentsResult {
	success: boolean;
	/** The updated document (without x-comment content) */
	document?: string;
	/** Array of annotation contents with updated messages */
	answers?: AnnotationContent[];
	/** IDs of annotations that were prompted */
	promptedIds?: string[];
	error?: string;
}

export interface ApplyCommentsOptions {
	/** Request ID for abort support (generated by caller for unified cancellation) */
	requestId: string;
	/** Called with progress updates during streaming */
	onProgress?: (info: { wordCount: number }) => void;
}

/**
 * Send annotations to OpenCode for processing.
 *
 * @param annotations - Array of annotation contents to process
 * @param document - The document HTML (without x-comment content)
 * @param options - Options including requestId for abort support and progress callback
 * @returns Result with updated document and answers
 */
export async function applyComments(
	annotations: AnnotationContent[],
	document: string,
	options: ApplyCommentsOptions
): Promise<ApplyCommentsResult> {
	try {
		webviewLogger.info(
			`applyComments: Starting annotation processing, annotationCount=${annotations.length}, requestId=${options.requestId}`
		);

		// Track which annotations were prompted (have new user content)
		// Only include annotations where the last message is from User with non-empty content
		const promptedIds = annotations
			.filter((a) => {
				if (a.messages.length === 0) return false;
				const lastMessage = a.messages[a.messages.length - 1];
				return lastMessage.author === 'User' && lastMessage.content.trim() !== '';
			})
			.map((a) => a.id);

		// 1. Format annotations into prompt list
		const commentList = formatAllPrompts(annotations);

		// 2. Convert document from HTML to Markdown (preserving x-annotation tags)
		const documentMd = documentHtmlToMarkdown(document);

		// 3. Build full prompt with both comment list and document
		const fullPrompt = buildPrompt(commentList, documentMd);

		// 4. Create OpenCode session (uses same requestId for unified abort)
		webviewLogger.info('applyComments: Creating OpenCode session');
		const sessionResult = await createSession('Annotation processing', options.requestId);

		if (sessionResult.error) {
			webviewLogger.error(`applyComments: Session creation failed: ${sessionResult.error}`);
			return { success: false, error: sessionResult.error };
		}

		if (!sessionResult.data?.id) {
			webviewLogger.error(`applyComments: No session ID found: ${JSON.stringify(sessionResult)}`);
			return { success: false, error: 'Failed to create session' };
		}

		const sessionId = sessionResult.data.id;
		webviewLogger.info(`applyComments: Session created, sessionId=${sessionId}`);

		// 5. Send prompt to session (uses same requestId for unified abort)
		webviewLogger.info(
			`applyComments: Sending prompt to OpenCode, hasOnProgress=${!!options.onProgress}`
		);
		const response = await sendPrompt(sessionId, fullPrompt, {
			requestId: options.requestId,
			onProgress: (wordCount) => {
				options.onProgress?.({ wordCount });
			}
		});

		// Check for error first (e.g., user cancelled)
		if (response.error) {
			webviewLogger.info(`applyComments: Error from OpenCode: ${response.error}`);
			return { success: false, error: response.error };
		}

		if (!response.data?.parts) {
			webviewLogger.error(
				`applyComments: No response parts from OpenCode: ${JSON.stringify(response)}`
			);
			return { success: false, error: 'No response from OpenCode' };
		}

		// 6. Extract text from response parts
		const texts = response.data.parts
			.filter((p) => p.type === 'text')
			.map((p) => ('text' in p ? p.text : ''))
			.filter(Boolean);

		const processedResponse = texts.join('\n').trim();

		// 7. Process answers and extract document markdown
		const { cleanedMarkdown, answers } = processAnswers(processedResponse);

		// 8. Convert document back from Markdown to HTML (preserving x-annotation tags)
		const documentHtml = documentMarkdownToHtml(cleanedMarkdown);

		webviewLogger.info(
			`applyComments: Processed answers, answerCount=${answers.length}, promptedCount=${promptedIds.length}`
		);

		return {
			success: true,
			document: documentHtml,
			answers,
			promptedIds
		};
	} catch (error) {
		const message = error instanceof Error ? error.message : 'Unknown error';
		const stack = error instanceof Error ? error.stack : '';
		webviewLogger.error(`applyComments: Error occurred: ${message}, stack=${stack}`);
		return { success: false, error: message };
	}
}
