import type { PluginInput } from "@opencode-ai/plugin"
import { log } from "../../shared/logger"
import { HOOK_NAME } from "./constants"
import { extractOracleSessionID, isOracleVerified } from "./oracle-verification-detector"
import type { RalphLoopState } from "./types"
import { handleFailedVerification } from "./verification-failure-handler"
import { withTimeout } from "./with-timeout"
import type { IterationCommitExpectation } from "./types"

type OpenCodeSessionMessage = {
	info?: { role?: string }
	parts?: Array<{ type?: string; text?: string }>
}

function collectAssistantText(message: OpenCodeSessionMessage): string {
	if (!Array.isArray(message.parts)) {
		return ""
	}

	let text = ""
	for (const part of message.parts) {
		if (part.type !== "text" && part.type !== "tool_result") {
			continue
		}
		text += `${text ? "\n" : ""}${part.text ?? ""}`
	}

	return text
}

async function detectOracleVerificationFromParentSession(
	ctx: PluginInput,
	parentSessionID: string,
	directory: string,
	apiTimeoutMs: number,
): Promise<string | undefined> {
	try {
		const response = await withTimeout(
			ctx.client.session.messages({
				path: { id: parentSessionID },
				query: { directory },
			}),
			apiTimeoutMs,
		)

		const messagesResponse: unknown = response
		const responseData =
			typeof messagesResponse === "object" && messagesResponse !== null && "data" in messagesResponse
				? (messagesResponse as { data?: unknown }).data
				: undefined
		const messageArray: unknown[] = Array.isArray(messagesResponse)
			? messagesResponse
			: Array.isArray(responseData)
				? responseData
				: []

		for (let index = messageArray.length - 1; index >= 0; index -= 1) {
			const message = messageArray[index] as OpenCodeSessionMessage
			if (message.info?.role !== "assistant") {
				continue
			}

			const assistantText = collectAssistantText(message)
			if (!isOracleVerified(assistantText)) {
				continue
			}

			const detectedOracleSessionID = extractOracleSessionID(assistantText)
			if (detectedOracleSessionID) {
				return detectedOracleSessionID
			}
		}

		return undefined
	} catch (error) {
		log(`[${HOOK_NAME}] Failed to scan parent session for oracle verification evidence`, {
			parentSessionID,
			error: String(error),
		})
		return undefined
	}
}

type LoopStateController = {
	restartAfterFailedVerification: (sessionID: string, messageCountAtStart?: number) => RalphLoopState | null
	clearVerificationState: (sessionID: string, messageCountAtStart?: number) => RalphLoopState | null
	incrementIteration: (expected?: IterationCommitExpectation) => RalphLoopState | null
	clear: () => boolean
	setVerificationSessionID: (sessionID: string, verificationSessionID: string) => RalphLoopState | null
}

export async function handlePendingVerification(
	ctx: PluginInput,
	input: {
		sessionID: string
		state: RalphLoopState
		verificationSessionID?: string
		matchesParentSession: boolean
		matchesVerificationSession: boolean
		loopState: LoopStateController
		directory: string
		apiTimeoutMs: number
	},
): Promise<void> {
	const {
		sessionID,
		state,
		verificationSessionID,
		matchesParentSession,
		matchesVerificationSession,
		loopState,
		directory,
		apiTimeoutMs,
	} = input

	if (matchesParentSession || (verificationSessionID && matchesVerificationSession)) {
		if (!verificationSessionID && state.session_id) {
			const recoveredVerificationSessionID = await detectOracleVerificationFromParentSession(
				ctx,
				state.session_id,
				directory,
				apiTimeoutMs,
			)

			if (recoveredVerificationSessionID) {
				const updatedState = loopState.setVerificationSessionID(
					state.session_id,
					recoveredVerificationSessionID,
				)
				if (updatedState) {
					log(`[${HOOK_NAME}] Recovered missing verification session from parent evidence`, {
						parentSessionID: state.session_id,
						recoveredVerificationSessionID,
					})
					return
				}
			}
		}

		const restarted = await handleFailedVerification(ctx, {
			state,
			loopState,
			directory,
			apiTimeoutMs,
		})
		if (restarted) {
			return
		}
	}

	log(`[${HOOK_NAME}] Waiting for oracle verification`, {
		sessionID,
		verificationSessionID,
		iteration: state.iteration,
	})
}
