/**
 * Anthropic (Claude) adapter for the Vvveb AI pipeline - /v1/messages API.
 *
 * Handles: text blocks, "thinking"/redacted_thinking reasoning blocks,
 * tool_use calls (input accumulated from input_json_delta stream deltas),
 * tool_result turns, image and PDF (document) blocks, usage stats and the
 * Anthropic error shape.
 */
(function (window) {
	"use strict";

	const Ai = window.VvvebAi;

	const DEFAULT_URL = 'https://api.anthropic.com/v1/messages';

	function buildContent(blocks) {
		const content = [];
		let textBuf = '';
		const flush = () => { if (textBuf) { content.push({ type: 'text', text: textBuf }); textBuf = ''; } };

		for (const b of blocks || []) {
			switch (b.type) {
				case 'text':
					textBuf += b.text;
					break;
				case 'image': {
					flush();
					if (b.url && !b.dataUrl) {
						content.push({ type: 'image', source: { type: 'url', url: b.url } });
					} else {
						content.push({ type: 'image', source: { type: 'base64', media_type: b.mediaType || 'image/png', data: Ai.dataUrlParts(b.dataUrl)[1] } });
					}
					break;
				}
				case 'file': {
					flush();
					if (b.fileId) {
						content.push({ type: 'document', source: { type: 'url', url: b.fileId }, title: b.filename || 'document' });
					} else {
						content.push({
							type: 'document',
							source: { type: 'base64', media_type: b.mediaType || 'application/pdf', data: Ai.dataUrlParts(b.dataUrl)[1] },
							title: b.filename || 'document'
						});
					}
					break;
				}
				case 'audio':
					// no native audio input on /v1/messages - degrade to a note
					textBuf += '[voice attachment: ' + (b.mediaType || 'audio') + ']\n';
					break;
				case 'thinking':
					flush();
					content.push({ type: 'thinking', thinking: b.text });
					break;
				case 'tool_call':
					flush();
					content.push({ type: 'tool_use', id: b.id, name: b.name, input: b.arguments || {} });
					break;
				case 'tool_result':
					flush();
					content.push({
						type: 'tool_result',
						tool_use_id: b.toolCallId,
						content: typeof b.plain === 'string' ? b.plain : JSON.stringify(b.plain == null ? '' : b.plain)
					});
					break;
			}
		}
		flush();
		if (!content.length) content.push({ type: 'text', text: '' });
		return content;
	}

	function buildMessages(messages) {
		const out = [];
		for (const m of messages || []) {
			if (m.role === 'system') continue; // sent as top-level `system`
			if (m.role === 'tool') {
				out.push({ role: 'user', content: [{ type: 'tool_result', tool_use_id: m.toolCallId, content: Ai.textOf(m) }] });
				continue;
			}
			out.push({ role: m.role === 'assistant' ? 'assistant' : 'user', content: buildContent(m.content) });
		}
		return out;
	}

	function buildRequest(payload, options) {
		options = options || {};
		const body = {
			model: payload.model || options.model || 'claude-sonnet-4-20250514',
			max_tokens: parseInt(payload.maxTokens || options.max_tokens, 10) || 1024,
			messages: buildMessages(payload.messages),
			stream: !!payload.stream
		};
		// anthropic temperature range is 0..1
		body.temperature = Ai.numOr(payload.temperature, options.temperature, 0, 1);
		if (payload.system) body.system = String(payload.system);
		if (payload.tools && payload.tools.length) {
			body.tools = payload.tools.map(t => {
				// support type:custom (e.g. code_exec) - map to Anthropic tool_use format
				let isCustom = (t.type||'').toLowerCase() === 'custom';
				if (isCustom){
					return {
						name: t.name,
						description: t.description || '',
						input_schema: t.parameters || t.input_schema || { type: 'object', properties: {} }
					};
				}
				return {
					name: t.name || t?.function?.name,
					description: t.description || t?.function?.description || '',
					input_schema: t.parameters || t.input_schema || t?.function?.parameters || { type: 'object', properties: {} }
				};
			});
			body.tool_choice = { type: 'auto' };
		}
		if (payload.thinking && payload.thinking.budgetTokens) {
			delete body.temperature; // not allowed together with extended thinking
			body.thinking = { type: 'enabled', budget_tokens: Math.max(1024, parseInt(payload.thinking.budgetTokens, 10) || 2048) };
		}

		const headers = { 'anthropic-version': '2023-06-01' };
		if (!options.omitAuth) {
			if (options.key) headers['x-api-key'] = options.key;
			// required for direct browser calls; harmless via proxy
			headers['anthropic-dangerous-direct-browser-access'] = 'true';
		}
		return { url: options.url || DEFAULT_URL, headers, body };
	}

	function parseResponse(data) {
		let text = '', thinking = '';
		const toolCalls = [];
		for (const b of data.content || []) {
			switch (b.type) {
				case 'text': text += b.text; break;
				case 'thinking': thinking += b.thinking || ''; break;
				case 'redacted_thinking': thinking += '[redacted thinking]\n'; break;
				case 'tool_use':
					toolCalls.push({ id: b.id, name: b.name, arguments: b.input || {} });
					break;
			}
		}
		// strip any tool-call XML that spilled into text/thinking
		if (text && /<(tool_calls|tool_call|invoke)/i.test(text)){
			let extra = Ai.extractToolCallsFromText ? Ai.extractToolCallsFromText(text) : [];
			for (let c of extra) toolCalls.push(c);
			text = Ai.stripToolCallTags ? Ai.stripToolCallTags(text) : text;
		}
		if (thinking && (/<(tool_calls|tool_call|invoke)/i.test(thinking) || /\[thinking|\[think|<think/i.test(thinking))){
			let extra = Ai.extractToolCallsFromText ? Ai.extractToolCallsFromText(thinking) : [];
			for (let c of extra) toolCalls.push(c);
			if (Ai.stripToolCallTags) thinking = Ai.stripToolCallTags(thinking);
			if (Ai.stripThinkingTags) thinking = Ai.stripThinkingTags(thinking);
		}
		return {
			text, thinking, toolCalls,
			usage: {
				inputTokens: data.usage && data.usage.input_tokens,
				outputTokens: data.usage && data.usage.output_tokens,
				cachedTokens: data.usage && (data.usage.cache_read_input_tokens ?? data.usage.cache_read ?? 0),
				cacheCreationTokens: data.usage && (data.usage.cache_creation_input_tokens ?? 0),
				totalTokens: data.usage && ((data.usage.input_tokens||0)+(data.usage.output_tokens||0))
			},
			stopReason: data.stop_reason,
			raw: data
		};
	}

	function createStreamParser(emit) {
		const blocks = new Map(); // index -> block accumulator
		let usage = {}, stopReason = null;

		const sse = Ai.createSseParser(function (data) {
			const json = Ai.safeJsonParse(data, null);
			if (!json || !json.type) return;

			switch (json.type) {
				case 'message_start':
					usage = (json.message && json.message.usage) || {};
					break;
				case 'content_block_start':
					blocks.set(json.index, {
						type: json.content_block.type,
						id: json.content_block.id,
						name: json.content_block.name || '',
						text: '',
						args: ''
					});
					break;
				case 'content_block_delta': {
					const b = blocks.get(json.index);
					const d = json.delta || {};
					if (!b) break;
					if (d.type === 'text_delta' && d.text) {
						let raw = d.text;
						if (Ai.stripToolCallTags && /<(tool_calls|tool_call|invoke)/i.test(raw)){
							let extracted = Ai.extractToolCallsFromText ? Ai.extractToolCallsFromText(raw) : [];
							for (let call of extracted) emit({ type: 'tool-call', call });
							let cleaned = Ai.stripToolCallTags(raw);
							if (cleaned && cleaned.trim()){ b.text += cleaned; emit({ type: 'text-delta', text: cleaned }); }
						} else {
							let cleaned = Ai.stripToolCallTags ? Ai.stripToolCallTags(raw) : raw;
							if (cleaned !== raw && !cleaned.trim() && raw.includes('<')){ /* partial tag, skip */ }
							else { b.text += cleaned; if (cleaned) emit({ type: 'text-delta', text: cleaned }); }
						}
					}
					else if (d.type === 'thinking_delta' && d.thinking) {
						let raw = d.thinking;
						let cleaned = Ai.stripToolCallTags ? Ai.stripToolCallTags(raw) : raw;
						if (cleaned !== raw){
							let extracted = Ai.extractToolCallsFromText ? Ai.extractToolCallsFromText(raw) : [];
							for (let call of extracted) emit({ type: 'tool-call', call });
							if (cleaned && cleaned.trim()){ b.text += cleaned; emit({ type: 'thinking-delta', text: cleaned }); }
						} else { b.text += raw; emit({ type: 'thinking-delta', text: raw }); }
					}
					else if (d.type === 'input_json_delta' && d.partial_json) b.args += d.partial_json;
					break;
				}
				case 'content_block_stop': {
					const b = blocks.get(json.index);
					if (b && b.type === 'tool_use') {
						emit({ type: 'tool-call', call: { id: b.id, name: b.name, arguments: Ai.safeJsonParse(b.args || '{}', {}) } });
					}
					break;
				}
				case 'message_delta':
					stopReason = json.delta && json.delta.stop_reason;
					if (json.usage) usage = Object.assign({}, usage, json.usage);
					break;
				case 'error':
					emit({ type: 'error', error: new Error(Ai.extractError(json)) });
					break;
			}
		});

		return {
			feed: sse.feed,
			end: sse.end,
			getResult() {
				let text = '', thinking = '';
				const toolCalls = [];
				for (const [, b] of [...blocks.entries()].sort((a, b) => a[0] - b[0])) {
					if (b.type === 'text') text += b.text;
					else if (b.type === 'thinking') thinking += b.text;
					else if (b.type === 'redacted_thinking') thinking += '[redacted thinking]\n';
					else if (b.type === 'tool_use') toolCalls.push({ id: b.id, name: b.name, arguments: Ai.safeJsonParse(b.args || '{}', {}) });
				}
				// final strip for any spillover that may have been accumulated in text/thinking blocks
				if (text && /<(tool_calls|tool_call|invoke)/i.test(text)){
					let extra = Ai.extractToolCallsFromText ? Ai.extractToolCallsFromText(text) : [];
					for (let c of extra) toolCalls.push(c);
					text = Ai.stripToolCallTags ? Ai.stripToolCallTags(text) : text;
				}
				if (thinking && /<(tool_calls|tool_call|invoke)/i.test(thinking)){
					let extra = Ai.extractToolCallsFromText ? Ai.extractToolCallsFromText(thinking) : [];
					for (let c of extra) toolCalls.push(c);
					thinking = Ai.stripToolCallTags ? Ai.stripToolCallTags(thinking) : thinking;
				}
				return {
					text, thinking, toolCalls,
					usage: { inputTokens: usage.input_tokens, outputTokens: usage.output_tokens, cachedTokens: usage.cache_read_input_tokens ?? 0, cacheCreationTokens: usage.cache_creation_input_tokens ?? 0, totalTokens: (usage.input_tokens||0)+(usage.output_tokens||0) },
					stopReason,
					raw: null
				};
			}
		};
	}

	function normalizeError(data) {
		// {"type":"error","error":{"type":"invalid_request_error","message":"..."}}
		return Ai.extractError(data);
	}

	Ai.registerProvider('anthropic', {
		name: 'anthropic',
		label: 'Anthropic',
		defaultModel: 'claude-sonnet-4-20250514',
		buildRequest,
		parseResponse,
		createStreamParser,
		normalizeError
	});
})(window);
