/**
 * PromptLayerLoader - Loads prompt layers from external resources
 *
 * Implements contracts §16; DD §10.2.
 * 4 methods: load_runtime_invariant (L0), load_role (L1, worker AgentType only),
 * load_project_rules (L3), load_task_context (L5).
 *
 * @module packages/runtime/src/context/PromptLayerLoader
 */

import { readFileSync, existsSync } from 'fs'
import { join, dirname } from 'path'
import { fileURLToPath } from 'url'
import type { PromptLayer, PromptLayerLevel, AgentType } from '@aircoding/contracts'

const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
const BUILTIN_PROMPTS_DIR = join(__dirname, '..', 'context', 'prompts')

export class PromptLayerLoader {
  private prompts_dir: string

  constructor(prompts_dir?: string) {
    this.prompts_dir = prompts_dir || BUILTIN_PROMPTS_DIR
  }

  /**
   * Load L0: Runtime invariant prompt.
   * Includes INV-1..5, core safety rules.
   */
  load_runtime_invariant(): PromptLayer {
    const path = join(this.prompts_dir, 'runtime_invariant.md')
    let content = this.read_or_default(path, DEFAULT_L0_INVARIANT)

    return {
      level: 'runtime_invariant',
      priority: 0,
      content,
      token_estimate: content.length / 4,
      source_ref: path,
      immutable: true
    }
  }

  /**
   * Load L1: Role-specific prompt.
   * Accepts only worker AgentType (executor/reviewer/debugger/compactor/experience_miner).
   * Runtime roles (main/architecture_designer/scheduler) load built-in directly.
   */
  load_role(role: AgentType): PromptLayer {
    const path = join(this.prompts_dir, 'roles', `${role}.md`)
    const content = this.read_or_default(path, default_role_prompt(role))

    return {
      level: 'role',
      priority: 1,
      content,
      token_estimate: content.length / 4,
      source_ref: path,
      immutable: false
    }
  }

  /**
   * Load L3: Project rules from .air/ directory.
   */
  load_project_rules(project: { project_id: string; project_root: string }): PromptLayer[] {
    const layers: PromptLayer[] = []

    // Load .air/shared/rules.md
    const shared_rules = join(project.project_root, '.air', 'shared', 'rules.md')
    if (existsSync(shared_rules)) {
      const content = readFileSync(shared_rules, 'utf-8')
      layers.push({
        level: 'project_rules',
        priority: 3,
        content,
        token_estimate: content.length / 4,
        source_ref: shared_rules
      })
    }

    // Load .air/local/rules.md (overrides)
    const local_rules = join(project.project_root, '.air', 'local', 'rules.md')
    if (existsSync(local_rules)) {
      const content = readFileSync(local_rules, 'utf-8')
      layers.push({
        level: 'project_rules',
        priority: 2, // higher than shared
        content,
        token_estimate: content.length / 4,
        source_ref: local_rules
      })
    }

    return layers
  }

  /**
   * Load L5: Task context (plan refs, arc refs, artifacts).
   */
  load_task_context(
    spec: {
      id: string
      type: string
      title: string
      description: string
      acceptance_criteria: string[]
    },
    context_refs: { plan_ref?: string; arc_ref?: string; artifacts?: string[] }
  ): PromptLayer[] {
    const layers: PromptLayer[] = []

    // Task spec layer
    const task_content = [
      `# Task: ${spec.title}`,
      `ID: ${spec.id}`,
      `Type: ${spec.type}`,
      '',
      `## Description`,
      spec.description,
      '',
      '## Acceptance Criteria',
      ...spec.acceptance_criteria.map((c, i) => `${i + 1}. ${c}`)
    ].join('\n')

    layers.push({
      level: 'task_spec',
      priority: 5,
      content: task_content,
      token_estimate: task_content.length / 4
    })

    // Plan reference
    if (context_refs.plan_ref) {
      const content = `# Implementation Plan\nRef: ${context_refs.plan_ref}`
      layers.push({
        level: 'task_spec',
        priority: 6,
        content,
        token_estimate: content.length / 4,
        source_ref: context_refs.plan_ref
      })
    }

    // Architecture reference
    if (context_refs.arc_ref) {
      const content = `# Architecture\nRef: ${context_refs.arc_ref}`
      layers.push({
        level: 'architecture',
        priority: 4,
        content,
        token_estimate: content.length / 4,
        source_ref: context_refs.arc_ref
      })
    }

    return layers
  }

  private read_or_default(path: string, default_content: string): string {
    if (existsSync(path)) {
      return readFileSync(path, 'utf-8')
    }
    return default_content
  }
}

// ============================================================================
// Default prompts (embedded as fallback when files not found)
// ============================================================================

const DEFAULT_L0_INVARIANT = `# Runtime Invariants (L0)

You are an AI coding assistant operating within the AirCoding v1.0.0 runtime.

## Core Invariants (INV-1..5)

### INV-1: Status Columns
Status columns are written ONLY by EventStore.project(). Repositories store data; they do NOT set status.
Never use repository.update() to change status — always emit an event instead.

### INV-2: Cross-DB Writes
Cross-DB writes MUST use the outbox model with a single writer.
Never write directly to tables in another database.

### INV-3: Side Effects
All side effects MUST go through ToolRegistry.call() → PermissionEngine.evaluate().
Never execute commands, write files, or access network directly.

### INV-4: Import Direction
Imports are one-way: contracts → runtime → other packages.
Never import from runtime into contracts.

### INV-5: EventBus is Transport
EventBus is a transport layer, NEVER a source of truth.
EventStore is the authoritative source. Never query EventBus for state.

## Safety Rules
- Never execute destructive commands without explicit confirmation
- Never access files outside the project workspace
- Never expose credentials, API keys, or secrets in output
- Always validate tool inputs before execution
`

function default_role_prompt(role: AgentType): string {
  const prompts: Record<AgentType, string> = {
    executor: `# Executor Role (L1)

You are an Executor agent responsible for implementing task specifications.

## Your responsibilities:
1. Read and understand the task specification
2. Implement the required changes
3. Run tests to verify correctness
4. Report completion status

## Rules:
- Follow the architecture defined in the implementation plan
- Use the FileSystem tools for code changes (read-before-edit enforced)
- Use Shell tools for building and testing
- Report any issues or blockers immediately
- NEVER make changes outside the project scope

## Output:
- Code changes with clear diffs
- Build/test results
- Completion status (pass/fail/blocked)
`,
    reviewer: `# Reviewer Role (L1)

You are a Reviewer agent responsible for code review and quality assurance.

## Your responsibilities:
1. Review code changes for correctness and style
2. Check for security vulnerabilities
3. Verify architecture compliance
4. Identify potential issues

## Rules:
- Check for INV-1..5 compliance
- Verify no direct side effects
- Check import direction compliance
- Flag any dropped or lost semantic information

## Output:
- Review findings with severity levels
- Suggested fixes for each issue
- Overall pass/fail verdict
`,
    debugger: `# Debugger Role (L1)

You are a Debugger agent responsible for diagnosing and fixing issues.

## Your responsibilities:
1. Analyze error reports and stack traces
2. Reproduce the issue in a controlled environment
3. Identify root cause
4. Propose and apply fixes

## Rules:
- Collect evidence (logs, traces, diagnostics)
- Verify fixes don't introduce regressions
- Use Debug tools for deep inspection
- Document findings for future reference

## Output:
- Root cause analysis
- Applied fix with explanation
- Evidence references
`,
    compactor: `# Compactor Role (L1)

You are a Compactor agent responsible for context compaction and memory management.

## Your responsibilities:
1. Monitor token usage and trigger compaction when needed
2. Generate concise summaries of conversation history
3. Archive old context while preserving critical information
4. Maintain referential integrity during compaction

## Rules:
- Never drop L0 (runtime_invariant) or L1 (role) layers
- Preserve all task specifications
- Keep evidence references intact
- Document what was compacted and why

## Output:
- Compaction summary
- Archived context references
- Updated context state
`,
    experience_miner: `# Experience Miner Role (L1)

You are an Experience Miner agent responsible for extracting patterns and learnings.

## Your responsibilities:
1. Analyze completed tasks for reusable patterns
2. Extract common failure modes and fixes
3. Identify architectural insights
4. Generate experience artifacts for future reference

## Rules:
- Only mine from completed/verified tasks
- Anonymize sensitive information
- Link to source tasks and evidence
- Categorize findings for easy lookup

## Output:
- Experience entries with categories
- Pattern descriptions
- Source references
`
  }
  return prompts[role] || `# ${role}\n\nRole prompt not yet defined.`
}

export function createPromptLayerLoader(prompts_dir?: string): PromptLayerLoader {
  return new PromptLayerLoader(prompts_dir)
}