package com.optix.android.domain.usecase

import com.optix.android.domain.model.ToolEvent
import com.optix.android.domain.model.ToolMetadata
import com.optix.android.domain.repository.AuditLogRepository
import com.optix.android.domain.repository.ToolRepository
import com.optix.android.tools.registry.ToolRegistry
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onStart
import timber.log.Timber
import javax.inject.Inject

class RunToolUseCase @Inject constructor(
    private val toolRepository: ToolRepository,
    private val auditLogRepository: AuditLogRepository,
    private val toolRegistry: ToolRegistry,
) {
    operator fun invoke(toolName: String, params: Map<String, Any>): Flow<ToolEvent> {
        val meta = toolRepository.getToolMetadata(toolName)
        val startedAt = System.currentTimeMillis()
        val safeParams = stripSecrets(params, meta)
        val target = extractTarget(params)

        return toolRepository.runTool(toolName, params)
            .onStart { /* execution begins */ }
            .catch { e ->
                recordAudit(toolName, meta, safeParams, target, startedAt, false, e.message)
                Timber.e(e, "Tool execution failed: %s", toolName)
                throw e
            }
            .onCompletion { cause ->
                if (cause == null) {
                    recordAudit(toolName, meta, safeParams, target, startedAt, true, null)
                }
            }
    }

    private fun recordAudit(
        toolName: String,
        meta: ToolMetadata?,
        safeParams: Map<String, String>,
        target: String?,
        startedAt: Long,
        success: Boolean,
        errorMessage: String?,
    ) {
        val durationMs = System.currentTimeMillis() - startedAt
        try {
            kotlinx.coroutines.runBlocking {
                auditLogRepository.insert(
                    com.optix.android.domain.model.AuditLogEntry(
                        toolName = toolName,
                        toolDisplayName = meta?.displayName ?: toolName,
                        params = safeParams,
                        target = target,
                        timestamp = startedAt,
                        success = success,
                        errorMessage = errorMessage,
                        durationMs = durationMs,
                    )
                )
            }
        } catch (e: Throwable) {
            Timber.e(e, "Failed to write audit log for %s", toolName)
        }
    }

    companion object {
        private val SECRET_KEY_HINTS = setOf("password", "passwd", "secret", "api_key", "apikey", "token", "key", "credential")

        fun stripSecrets(params: Map<String, Any>, meta: ToolMetadata?): Map<String, String> {
            val sensitiveNames = meta?.parameters
                ?.filter { it.type.name == "SECRET" || isSecretName(it.name) }
                ?.map { it.name }
                .orEmpty() + params.keys.filter { isSecretName(it) }

            return params.mapNotNull { (k, v) ->
                if (k in sensitiveNames) null else k to v.toString()
            }.toMap()
        }

        fun extractTarget(params: Map<String, Any>): String? {
            return params.entries.firstOrNull { (k, _) ->
                val n = k.lowercase()
                n.contains("target") || n.contains("host") || n == "ip" ||
                    (n.contains("ip") && !n.contains("subnet"))
            }?.value?.toString()
        }

        private fun isSecretName(name: String): Boolean {
            val n = name.lowercase()
            return SECRET_KEY_HINTS.any { n.contains(it) }
        }
    }
}
