/**
 * Trust Engine — tracks tool success/failure and computes trust scores.
 */

import { readFile, writeFile, mkdir } from 'fs/promises'
import { join } from 'path'
import { homedir } from 'os'
import { logForDebugging } from '../../utils/debug.js'
import {
  type CategoryTrust,
  type ToolCategory,
  type TrustProfile,
  type TrustState,
  createDefaultTrustProfile,
  createFreshTrustState,
} from './types.js'

// Trust curve parameters
const SUCCESS_INCREMENT = 0.02
const FAILURE_PENALTY = 0.2
const TIME_DECAY_PER_DAY = 0.01
const AUTO_APPROVE_THRESHOLD = 0.7

// Tools that should NEVER be auto-approved regardless of trust
const NEVER_AUTO_APPROVE = new Set([
  'git push',
  'rm -rf',
  'git reset --hard',
  'git push --force',
  'git clean',
  'drop table',
  'drop database',
])

const TRUST_FILE = join(homedir(), '.sandcode', 'trust-scores.json')

/** Tool name → category mapping */
const TOOL_CATEGORIES: Record<string, ToolCategory> = {
  Read: 'read',
  FileReadTool: 'read',
  Glob: 'read',
  GlobTool: 'read',
  Grep: 'read',
  GrepTool: 'read',
  LSP: 'read',
  LSPTool: 'read',

  Edit: 'write',
  FileEditTool: 'write',
  Write: 'write',
  FileWriteTool: 'write',
  NotebookEdit: 'write',
  NotebookEditTool: 'write',

  Bash: 'execute',
  BashTool: 'execute',

  WebFetch: 'external',
  WebFetchTool: 'external',
  WebSearch: 'external',
  WebSearchTool: 'external',
  MCPTool: 'external',
}

/**
 * Get the tool category for a tool name.
 */
export function getToolCategory(toolName: string): ToolCategory {
  return TOOL_CATEGORIES[toolName] ?? 'external'
}

/**
 * Load trust state from disk.
 */
export async function loadTrustState(): Promise<TrustState> {
  try {
    const content = await readFile(TRUST_FILE, 'utf-8')
    const parsed = JSON.parse(content) as TrustState
    if (parsed.version === 1) return parsed
  } catch {
    // File doesn't exist
  }
  return createFreshTrustState()
}

/**
 * Save trust state to disk.
 */
async function saveTrustState(state: TrustState): Promise<void> {
  await mkdir(join(homedir(), '.sandcode'), { recursive: true })
  await writeFile(TRUST_FILE, JSON.stringify(state, null, 2), 'utf-8')
}

/**
 * Get or create the trust profile for a project.
 */
export async function getTrustProfile(
  projectPath: string,
): Promise<TrustProfile> {
  const state = await loadTrustState()
  let profile = state.profiles[projectPath]
  if (!profile) {
    profile = createDefaultTrustProfile(projectPath)
    state.profiles[projectPath] = profile
    await saveTrustState(state)
  }
  return applyTimeDecay(profile)
}

/**
 * Record a successful tool use.
 */
export async function recordSuccess(
  projectPath: string,
  toolName: string,
): Promise<void> {
  const category = getToolCategory(toolName)
  const state = await loadTrustState()

  if (!state.profiles[projectPath]) {
    state.profiles[projectPath] = createDefaultTrustProfile(projectPath)
  }

  const trust = state.profiles[projectPath]!.tools[category]
  trust.successes++
  trust.score = Math.min(1.0, trust.score + SUCCESS_INCREMENT)
  state.profiles[projectPath]!.lastUpdated = Date.now()

  await saveTrustState(state)
}

/**
 * Record a failed tool use.
 */
export async function recordFailure(
  projectPath: string,
  toolName: string,
): Promise<void> {
  const category = getToolCategory(toolName)
  const state = await loadTrustState()

  if (!state.profiles[projectPath]) {
    state.profiles[projectPath] = createDefaultTrustProfile(projectPath)
  }

  const trust = state.profiles[projectPath]!.tools[category]
  trust.failures++
  trust.lastFailure = Date.now()
  trust.score = Math.max(0.0, trust.score - FAILURE_PENALTY)
  state.profiles[projectPath]!.lastUpdated = Date.now()

  await saveTrustState(state)
  logForDebugging(
    `Trust failure recorded: ${toolName} (${category}), new score: ${trust.score.toFixed(2)}`,
  )
}

/**
 * Check if a tool use should be auto-approved based on trust.
 */
export async function shouldAutoApprove(
  projectPath: string,
  toolName: string,
  commandContent?: string,
): Promise<{ approved: boolean; score: number; reason?: string }> {
  // Check for never-auto-approve commands
  if (commandContent) {
    for (const dangerous of NEVER_AUTO_APPROVE) {
      if (commandContent.includes(dangerous)) {
        return {
          approved: false,
          score: 0,
          reason: `"${dangerous}" is never auto-approved`,
        }
      }
    }
  }

  const profile = await getTrustProfile(projectPath)
  const category = getToolCategory(toolName)
  const trust = profile.tools[category]

  if (trust.score >= AUTO_APPROVE_THRESHOLD) {
    return { approved: true, score: trust.score }
  }

  return {
    approved: false,
    score: trust.score,
    reason: `Trust score ${(trust.score * 100).toFixed(0)}% below threshold ${(AUTO_APPROVE_THRESHOLD * 100).toFixed(0)}%`,
  }
}

/**
 * Reset trust for a project.
 */
export async function resetTrust(projectPath: string): Promise<void> {
  const state = await loadTrustState()
  state.profiles[projectPath] = createDefaultTrustProfile(projectPath)
  await saveTrustState(state)
  logForDebugging(`Trust reset for ${projectPath}`)
}

/**
 * Format trust status for display.
 */
export async function formatTrustStatus(
  projectPath: string,
): Promise<string> {
  const profile = await getTrustProfile(projectPath)
  const lines = [`Trust Status for ${projectPath}`, '']

  for (const [category, trust] of Object.entries(profile.tools) as Array<
    [ToolCategory, CategoryTrust]
  >) {
    const pct = (trust.score * 100).toFixed(0)
    const bar = '█'.repeat(Math.round(trust.score * 10)) +
      '░'.repeat(10 - Math.round(trust.score * 10))
    const autoApprove =
      trust.score >= AUTO_APPROVE_THRESHOLD ? ' ◈ auto-approve' : ''
    lines.push(
      `  ${category.padEnd(10)} ${bar} ${pct}% (${trust.successes}✓ ${trust.failures}✗)${autoApprove}`,
    )
  }

  return lines.join('\n')
}

/**
 * Apply time decay to trust scores.
 */
function applyTimeDecay(profile: TrustProfile): TrustProfile {
  const now = Date.now()
  const daysSinceUpdate = (now - profile.lastUpdated) / (24 * 60 * 60 * 1000)

  if (daysSinceUpdate < 1) return profile

  const decay = daysSinceUpdate * TIME_DECAY_PER_DAY

  for (const category of Object.keys(profile.tools) as ToolCategory[]) {
    profile.tools[category].score = Math.max(
      0.0,
      profile.tools[category].score - decay,
    )
  }

  return profile
}
