/**
 * Instinct Store
 *
 * Reads and writes instincts to .sandcode/instincts/ (project-local)
 * and ~/.sandcode/knowledge/instincts/ (global).
 *
 * Instincts are stored as markdown files with YAML frontmatter,
 * making them human-readable and editable.
 */

import { readFile, writeFile, mkdir, readdir } from 'fs/promises'
import { join } from 'path'
import { getCwd } from '../../utils/cwd.js'
import { logForDebugging } from '../../utils/debug.js'
import type { Instinct } from './types.js'

const INSTINCTS_DIR = '.sandcode/instincts'
const GLOBAL_INSTINCTS_DIR = 'knowledge/instincts'

/**
 * Save an instinct to disk.
 */
export async function saveInstinct(instinct: Instinct): Promise<void> {
  const dir = instinct.scope === 'project'
    ? join(getCwd(), INSTINCTS_DIR)
    : join(getHomeDir(), GLOBAL_INSTINCTS_DIR)

  await mkdir(dir, { recursive: true })

  const filename = `${instinct.name}.md`
  const filepath = join(dir, filename)
  const content = formatInstinctAsMarkdown(instinct)

  await writeFile(filepath, content, 'utf-8')
  logForDebugging(`Instinct saved: ${instinct.name} (${instinct.scope})`)
}

/**
 * Load an instinct by name.
 */
export async function loadInstinct(
  name: string,
  scope: 'project' | 'global' = 'project',
): Promise<Instinct | null> {
  const dir = scope === 'project'
    ? join(getCwd(), INSTINCTS_DIR)
    : join(getHomeDir(), GLOBAL_INSTINCTS_DIR)

  try {
    const filepath = join(dir, `${name}.md`)
    const content = await readFile(filepath, 'utf-8')
    return parseInstinctFromMarkdown(content, scope)
  } catch {
    return null
  }
}

/**
 * Load all instincts (project-local + global).
 */
export async function loadAllInstincts(): Promise<Instinct[]> {
  const instincts: Instinct[] = []

  // Load project-local instincts
  const projectInstincts = await loadInstinctsFromDir(
    join(getCwd(), INSTINCTS_DIR),
    'project',
  )
  instincts.push(...projectInstincts)

  // Load global instincts
  const globalInstincts = await loadInstinctsFromDir(
    join(getHomeDir(), GLOBAL_INSTINCTS_DIR),
    'global',
  )
  instincts.push(...globalInstincts)

  return instincts
}

/**
 * Find instincts that match given trigger words.
 */
export async function findMatchingInstincts(
  text: string,
): Promise<Instinct[]> {
  const allInstincts = await loadAllInstincts()
  const lower = text.toLowerCase()

  return allInstincts.filter(instinct =>
    instinct.triggers.some(trigger => lower.includes(trigger.toLowerCase())),
  )
}

/**
 * Increment the usage count of an instinct.
 */
export async function incrementUsageCount(name: string, scope: 'project' | 'global' = 'project'): Promise<void> {
  const instinct = await loadInstinct(name, scope)
  if (!instinct) return

  instinct.usageCount++
  instinct.lastUsed = Date.now()

  // Promote to global if used enough times in project
  if (scope === 'project' && instinct.usageCount >= 5) {
    instinct.scope = 'global'
    instinct.confidence = Math.min(1, instinct.confidence + 0.1)
  }

  await saveInstinct(instinct)
}

/**
 * Format an instinct as markdown.
 */
function formatInstinctAsMarkdown(instinct: Instinct): string {
  const lines: string[] = []

  lines.push('---')
  lines.push(`name: ${instinct.name}`)
  lines.push(`confidence: ${instinct.confidence}`)
  lines.push(`usage_count: ${instinct.usageCount}`)
  lines.push(`triggers: [${instinct.triggers.map(t => `"${t}"`).join(', ')}]`)
  lines.push(`scope: ${instinct.scope}`)
  lines.push(`created_at: ${instinct.createdAt}`)
  lines.push(`last_used: ${instinct.lastUsed}`)
  lines.push('---')
  lines.push('')
  lines.push(`# ${instinct.name}`)
  lines.push('')

  lines.push('## Steps')
  for (let i = 0; i < instinct.steps.length; i++) {
    lines.push(`${i + 1}. ${instinct.steps[i]}`)
  }
  lines.push('')

  if (instinct.gotchas.length > 0) {
    lines.push('## Gotchas')
    for (const gotcha of instinct.gotchas) {
      lines.push(`- ${gotcha}`)
    }
    lines.push('')
  }

  return lines.join('\n')
}

/**
 * Parse an instinct from markdown.
 */
function parseInstinctFromMarkdown(content: string, scope: 'project' | 'global'): Instinct {
  const defaults: Instinct = {
    name: 'unknown',
    triggers: [],
    confidence: 0.5,
    usageCount: 0,
    createdAt: Date.now(),
    lastUsed: Date.now(),
    steps: [],
    gotchas: [],
    scope,
  }

  // Parse YAML frontmatter
  const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/)
  if (frontmatterMatch?.[1]) {
    const fm = frontmatterMatch[1]

    const nameMatch = fm.match(/name:\s*(.+)/)
    if (nameMatch) defaults.name = nameMatch[1]!.trim()

    const confidenceMatch = fm.match(/confidence:\s*([\d.]+)/)
    if (confidenceMatch) defaults.confidence = parseFloat(confidenceMatch[1]!)

    const usageMatch = fm.match(/usage_count:\s*(\d+)/)
    if (usageMatch) defaults.usageCount = parseInt(usageMatch[1]!, 10)

    const triggersMatch = fm.match(/triggers:\s*\[(.+)\]/)
    if (triggersMatch) {
      defaults.triggers = triggersMatch[1]!
        .split(',')
        .map(t => t.trim().replace(/"/g, ''))
    }

    const createdAtMatch = fm.match(/created_at:\s*(\d+)/)
    if (createdAtMatch) defaults.createdAt = parseInt(createdAtMatch[1]!, 10)

    const lastUsedMatch = fm.match(/last_used:\s*(\d+)/)
    if (lastUsedMatch) defaults.lastUsed = parseInt(lastUsedMatch[1]!, 10)
  }

  // Parse steps from body
  const stepsSection = content.match(/## Steps\n([\s\S]*?)(?:\n##|\n*$)/)
  if (stepsSection?.[1]) {
    defaults.steps = stepsSection[1]
      .split('\n')
      .map(l => l.trim())
      .filter(l => /^\d+\.\s/.test(l))
      .map(l => l.replace(/^\d+\.\s*/, ''))
  }

  // Parse gotchas
  const gotchasSection = content.match(/## Gotchas\n([\s\S]*?)(?:\n##|\n*$)/)
  if (gotchasSection?.[1]) {
    defaults.gotchas = gotchasSection[1]
      .split('\n')
      .map(l => l.trim())
      .filter(l => l.startsWith('- '))
      .map(l => l.replace(/^- /, ''))
  }

  return defaults
}

/**
 * Load instincts from a directory.
 */
async function loadInstinctsFromDir(
  dir: string,
  scope: 'project' | 'global',
): Promise<Instinct[]> {
  try {
    const files = await readdir(dir)
    const instincts: Instinct[] = []

    for (const file of files) {
      if (!file.endsWith('.md')) continue
      try {
        const content = await readFile(join(dir, file), 'utf-8')
        const instinct = parseInstinctFromMarkdown(content, scope)
        instincts.push(instinct)
      } catch {
        // Skip malformed files
      }
    }

    return instincts
  } catch {
    return []
  }
}

/**
 * Get home directory.
 */
function getHomeDir(): string {
  return process.env.HOME ?? process.env.USERPROFILE ?? '~'
}
