package com.correx.apps.server.lifecycle

import com.correx.apps.server.ServerModule
import com.correx.apps.server.registry.ProviderRegistry
import com.correx.apps.server.registry.WorkflowRegistry
import com.correx.apps.server.registry.WorkflowSummary
import com.correx.apps.server.undo.SessionUndoService
import com.correx.core.approvals.domain.DefaultApprovalEngine
import com.correx.core.artifactstore.ArtifactStore
import com.correx.core.context.builder.DefaultContextPackBuilder
import com.correx.core.context.compression.DefaultContextCompressor
import com.correx.core.events.EventDispatcher
import com.correx.core.events.stores.EventStore
import com.correx.core.events.types.ArtifactId
import com.correx.core.inference.InferenceProjector
import com.correx.core.inference.InferenceProvider
import com.correx.core.inference.InferenceRepository
import com.correx.core.kernel.orchestration.DefaultOrchestrationReducer
import com.correx.core.kernel.orchestration.DefaultSessionOrchestrator
import com.correx.core.kernel.orchestration.OrchestrationConfig
import com.correx.core.kernel.orchestration.OrchestrationProjector
import com.correx.core.kernel.orchestration.OrchestrationRepository
import com.correx.core.kernel.orchestration.OrchestratorEngines
import com.correx.core.kernel.orchestration.OrchestratorRepositories
import com.correx.core.kernel.retry.DefaultRetryCoordinator
import com.correx.core.risk.DefaultRiskAssessor
import com.correx.core.talkie.model.TalkieConfig
import com.correx.core.sessions.DefaultSessionReducer
import com.correx.core.sessions.DefaultSessionRepository
import com.correx.core.sessions.SessionProjector
import com.correx.core.sessions.projections.replay.DefaultEventReplayer
import com.correx.core.transitions.graph.WorkflowGraph
import com.correx.core.transitions.resolution.DefaultTransitionResolver
import com.correx.core.validation.pipeline.ValidationPipeline
import com.correx.infrastructure.InfrastructureModule
import com.correx.infrastructure.inference.DefaultProviderRegistry
import com.correx.infrastructure.inference.commons.ManagedInferenceRouter
import com.correx.infrastructure.inference.commons.ResourceProbe
import com.correx.infrastructure.inference.commons.UnavailableProbe
import com.correx.infrastructure.inference.llama.cpp.DefaultModelManager
import com.correx.infrastructure.persistence.InMemoryEventStore
import com.correx.infrastructure.tools.FileEditConfig
import com.correx.infrastructure.tools.FileReadConfig
import com.correx.infrastructure.tools.FileWriteConfig
import com.correx.infrastructure.tools.ShellConfig
import com.correx.infrastructure.tools.ToolConfig
import io.ktor.client.HttpClient
import io.ktor.client.engine.mock.MockEngine
import io.ktor.client.engine.mock.respond
import io.ktor.http.ContentType
import io.ktor.http.HttpHeaders
import io.ktor.http.headersOf
import java.nio.file.Path

/**
 * Builds a fully-wired [ServerModule] on the managed-model path, using an in-memory event store
 * and a no-op artifact store. The [modelSwapper] and [resourceProbe] are injected externally so
 * tests can supply stubs or real implementations.
 *
 * The heavy infrastructure (SQLite, CAS, real tool executors) is deliberately avoided: the
 * lifecycle tests only drive model-swap WS messages and never start sessions or invoke tools.
 */
fun buildTestServerModule(
    eventStore: EventStore,
    modelSwapper: ManagedInferenceRouter,
    resourceProbe: ResourceProbe = UnavailableProbe,
    tempDir: Path,
): ServerModule {
    val artifactStore = noopArtifactStore()
    val eventDispatcher = EventDispatcher(eventStore)

    val firstProvider: InferenceProvider = InfrastructureModule.createLlamaCppProvider(
        modelId = modelSwapper.availableModelIds().first(),
        modelPath = "/dev/null",
        baseUrl = "http://127.0.0.1:1",
    )

    val infraRegistry: DefaultProviderRegistry = InfrastructureModule.createProviderRegistry(listOf(firstProvider))

    val toolConfig = ToolConfig(
        shell = ShellConfig(enabled = false, allowedExecutables = emptySet(), workingDir = tempDir),
        fileRead = FileReadConfig(enabled = false, allowedPaths = setOf(tempDir)),
        fileWrite = FileWriteConfig(enabled = false, allowedPaths = setOf(tempDir), workingDir = tempDir),
        fileEdit = FileEditConfig(enabled = false, allowedPaths = setOf(tempDir), workingDir = tempDir),
    )
    val toolRegistry = InfrastructureModule.createToolRegistry(toolConfig)
    val toolExecutor = InfrastructureModule.createToolExecutor(
        registry = toolRegistry,
        eventDispatcher = eventDispatcher,
        workDir = tempDir,
        artifactStore = null,
    )

    val approvalEngine = DefaultApprovalEngine()

    val engines = OrchestratorEngines(
        transitionResolver = DefaultTransitionResolver { condition, ctx -> condition.evaluate(ctx) },
        contextPackBuilder = DefaultContextPackBuilder(DefaultContextCompressor()),
        inferenceRouter = modelSwapper,
        validationPipeline = ValidationPipeline(validators = emptyList()),
        approvalEngine = approvalEngine,
        riskAssessor = DefaultRiskAssessor(),
        toolRegistry = toolRegistry,
        toolExecutor = toolExecutor,
    )

    val repositories = buildRepositories(eventStore)

    val decisionJournalRepository = InfrastructureModule.createDecisionJournalRepository(eventStore)
    val orchestrator = DefaultSessionOrchestrator(
        repositories = repositories,
        engines = engines,
        retryCoordinator = DefaultRetryCoordinator(eventStore),
        artifactStore = artifactStore,
        tokenizer = firstProvider.tokenizer,
        decisionJournalRepository = decisionJournalRepository,
    )

    val routerFacade = InfrastructureModule.createTalkieFacade(
        eventStore = eventStore,
        inferenceRouter = modelSwapper,
        config = TalkieConfig(),
        tokenizer = firstProvider.tokenizer,
    )

    val sessionUndoService = SessionUndoService(
        eventStore = eventStore,
        artifactStore = artifactStore,
        bootRoots = setOf(tempDir),
    )

    val providerRegistry: ProviderRegistry = object : ProviderRegistry {
        override fun listAll() = infraRegistry.listAll()
        override suspend fun healthCheckAll() = infraRegistry.healthCheckAll()
    }

    val noopWorkflowRegistry = object : WorkflowRegistry {
        override fun listAll(): List<WorkflowSummary> = emptyList()
        override fun find(workflowId: String): WorkflowGraph? = null
    }

    return ServerModule(
        orchestrator = orchestrator,
        eventStore = eventStore,
        artifactStore = artifactStore,
        sessionRepository = repositories.sessionRepository,
        workflowRegistry = noopWorkflowRegistry,
        providerRegistry = providerRegistry,
        defaultOrchestrationConfig = OrchestrationConfig(sandboxRoot = tempDir),
        routerFacade = routerFacade,
        orchestrationRepository = repositories.orchestrationRepository,
        approvalRepository = repositories.approvalRepository,
        toolRegistry = toolRegistry,
        sessionUndoService = sessionUndoService,
        modelSwapper = modelSwapper,
        resourceProbe = resourceProbe,
    )
}

private fun buildRepositories(eventStore: EventStore): OrchestratorRepositories =
    OrchestratorRepositories(
        eventStore = eventStore,
        inferenceRepository = InferenceRepository(DefaultEventReplayer(eventStore, InferenceProjector())),
        orchestrationRepository = OrchestrationRepository(
            DefaultEventReplayer(eventStore, OrchestrationProjector(DefaultOrchestrationReducer())),
        ),
        sessionRepository = DefaultSessionRepository(
            DefaultEventReplayer(eventStore, SessionProjector(DefaultSessionReducer())),
        ),
        artifactRepository = InfrastructureModule.createArtifactRepository(eventStore),
        approvalRepository = InfrastructureModule.createApprovalRepository(eventStore),
    )

private fun noopArtifactStore(): ArtifactStore = object : ArtifactStore {
    override suspend fun put(bytes: ByteArray): ArtifactId = ArtifactId("noop")
    override suspend fun get(id: ArtifactId): ByteArray? = null
    override suspend fun flushBefore(commit: suspend () -> Unit) = commit()
}

fun buildMockHttpClient(): HttpClient {
    val engine = MockEngine {
        respond(
            content = """{"status":"healthy"}""",
            headers = headersOf(HttpHeaders.ContentType, ContentType.Application.Json.toString()),
        )
    }
    return HttpClient(engine)
}

fun buildTestModelManager(
    llamaBin: String,
    host: String,
    port: Int,
    healthTimeoutMs: Long,
    eventStore: EventStore,
    httpClient: HttpClient,
): DefaultModelManager {
    val dispatcher = EventDispatcher(eventStore)
    return DefaultModelManager(
        llamaServerBin = llamaBin,
        host = host,
        port = port,
        healthTimeoutMs = healthTimeoutMs,
        eventStore = eventStore,
        httpClient = httpClient,
        eventDispatcher = dispatcher,
    )
}

fun buildInMemoryEventStore(): InMemoryEventStore = InMemoryEventStore()
