/**
 * Behavioral Rules Engine — CRUD operations and confidence management.
 */

import { readFile, writeFile, mkdir } from 'fs/promises'
import { randomUUID } from 'crypto'
import { join } from 'path'
import { getCwd } from '../../utils/cwd.js'
import { logForDebugging } from '../../utils/debug.js'
import {
  type BehaviorRule,
  type BehaviorRulesState,
  createFreshRulesState,
} from './types.js'

const RULES_FILE = '.sandcode/behavior-rules.json'
const MAX_RULES = 50
const CONFIDENCE_DECAY_PER_WEEK = 0.05
const MIN_CONFIDENCE = 0.1

function getRulesPath(): string {
  return join(getCwd(), RULES_FILE)
}

/**
 * Load behavior rules from disk.
 */
export async function loadRules(): Promise<BehaviorRulesState> {
  try {
    const content = await readFile(getRulesPath(), 'utf-8')
    const parsed = JSON.parse(content) as BehaviorRulesState
    if (parsed.version === 1) {
      return applyConfidenceDecay(parsed)
    }
  } catch {
    // File doesn't exist or invalid
  }
  return createFreshRulesState()
}

/**
 * Save behavior rules to disk.
 */
export async function saveRules(state: BehaviorRulesState): Promise<void> {
  await mkdir(join(getCwd(), '.sandcode'), { recursive: true })
  await writeFile(getRulesPath(), JSON.stringify(state, null, 2), 'utf-8')
}

/**
 * Add a new behavior rule.
 */
export async function addRule(
  rule: Omit<BehaviorRule, 'id' | 'createdAt' | 'lastReinforcedAt'>,
): Promise<BehaviorRule> {
  const state = await loadRules()

  // Check for duplicate trigger+action
  const existing = state.rules.find(
    (r) => r.trigger === rule.trigger && r.action === rule.action,
  )
  if (existing) {
    // Reinforce existing rule
    existing.confidence = Math.min(1.0, existing.confidence + 0.1)
    existing.lastReinforcedAt = Date.now()
    await saveRules(state)
    logForDebugging(`Reinforced behavior rule: ${existing.trigger} → ${existing.action}`)
    return existing
  }

  const newRule: BehaviorRule = {
    ...rule,
    id: randomUUID().slice(0, 8),
    createdAt: Date.now(),
    lastReinforcedAt: Date.now(),
  }

  state.rules.push(newRule)

  // Keep only top MAX_RULES by confidence
  if (state.rules.length > MAX_RULES) {
    state.rules.sort((a, b) => b.confidence - a.confidence)
    state.rules = state.rules.slice(0, MAX_RULES)
  }

  await saveRules(state)
  logForDebugging(`Added behavior rule: ${newRule.trigger} → ${newRule.action}`)
  return newRule
}

/**
 * Delete a rule by ID.
 */
export async function deleteRule(id: string): Promise<boolean> {
  const state = await loadRules()
  const before = state.rules.length
  state.rules = state.rules.filter((r) => r.id !== id)
  if (state.rules.length < before) {
    await saveRules(state)
    return true
  }
  return false
}

/**
 * Get the top N rules by confidence for system prompt injection.
 */
export async function getTopRules(n: number = 10): Promise<BehaviorRule[]> {
  const state = await loadRules()
  return state.rules
    .filter((r) => r.confidence >= MIN_CONFIDENCE)
    .sort((a, b) => b.confidence - a.confidence)
    .slice(0, n)
}

/**
 * Format rules for system prompt injection (~200 tokens max).
 */
export async function formatRulesForPrompt(): Promise<string | null> {
  const rules = await getTopRules(10)
  if (rules.length === 0) return null

  const lines = ['<behavior-rules>']
  for (const rule of rules) {
    const confidence = Math.round(rule.confidence * 100)
    lines.push(
      `- ${rule.trigger}: ${rule.action} [${confidence}%${rule.context !== 'global' ? `, scope: ${rule.context}` : ''}]`,
    )
  }
  lines.push('</behavior-rules>')
  return lines.join('\n')
}

/**
 * Apply confidence decay based on time since last reinforcement.
 */
function applyConfidenceDecay(
  state: BehaviorRulesState,
): BehaviorRulesState {
  const now = Date.now()
  const ONE_WEEK = 7 * 24 * 60 * 60 * 1000

  for (const rule of state.rules) {
    const weeksSinceReinforcement =
      (now - rule.lastReinforcedAt) / ONE_WEEK
    const decay = weeksSinceReinforcement * CONFIDENCE_DECAY_PER_WEEK
    rule.confidence = Math.max(MIN_CONFIDENCE, rule.confidence - decay)
  }

  // Remove rules below minimum confidence
  state.rules = state.rules.filter((r) => r.confidence >= MIN_CONFIDENCE)

  return state
}
