#!/usr/bin/env bash
set -euo pipefail

# ============================================================
# XH-1 RESEARCH HARNESS
#
# Multi-provider / multi-model autonomous research system.
#
# Supports any OpenAI-compatible Chat Completions API:
#
#   OpenRouter
#   Ollama
#   LM Studio
#   vLLM
#   LocalAI
#   OpenAI
#   Other compatible providers
#
# Pipeline:
#
#   RESEARCH
#       ↓
#   REVIEW
#       ↓
#   PASS ───────────────→ COMMIT
#       │
#      FAIL
#       ↓
#   REVISION
#       ↓
#   REVIEW
#       ↓
#   ...
#
# ============================================================

ROOT="${XH1_RESEARCH_ROOT:-research}"
CONFIG="${XH1_RESEARCH_CONFIG:-xh1-research.conf}"
STATE="${ROOT}/.xh1"

mkdir -p \
    "$STATE/logs" \
    "$STATE/runs" \
    "$STATE/responses"

# ============================================================
# Defaults
# ============================================================

MAX_RESEARCH_ROUNDS="${MAX_RESEARCH_ROUNDS:-3}"
MAX_API_RETRIES="${MAX_API_RETRIES:-3}"
DELAY_SECONDS="${DELAY_SECONDS:-5}"
MAX_ITERATIONS="${MAX_ITERATIONS:-0}"

REVIEW_ENABLED="${REVIEW_ENABLED:-true}"
AUTO_COMMIT="${AUTO_COMMIT:-false}"

RUN_ID=""

# ============================================================
# Helpers
# ============================================================

die() {
    echo "ERROR: $*" >&2
    exit 1
}

log() {
    echo "[$(date -u '+%Y-%m-%dT%H:%M:%SZ')] $*" |
        tee -a "$STATE/logs/harness.log"
}

require() {
    command -v "$1" >/dev/null 2>&1 ||
        die "Required command not found: $1"
}

require_dependencies() {
    require bash
    require curl
    require jq
    require find
    require grep
    require sed
    require awk
}

timestamp() {
    date -u '+%Y%m%dT%H%M%SZ'
}

load_config() {
    if [[ -f "$CONFIG" ]]; then
        # shellcheck disable=SC1090
        source "$CONFIG"
    fi
}

# ============================================================
# Provider abstraction
# ============================================================

#
# Provider variables are defined in xh1-research.conf.
#
# Example:
#
# PROVIDER_openrouter_URL="https://openrouter.ai/api/v1"
# PROVIDER_openrouter_KEY_ENV="OPENROUTER_API_KEY"
#
# PROVIDER_ollama_URL="http://127.0.0.1:11434/v1"
# PROVIDER_ollama_KEY_ENV=""
#
# PROVIDER_openai_URL="https://api.openai.com/v1"
# PROVIDER_openai_KEY_ENV="OPENAI_API_KEY"
#

provider_url() {
    local provider="$1"

    local variable="PROVIDER_${provider}_URL"

    printf '%s' "${!variable:-}"
}

provider_key_env() {
    local provider="$1"

    local variable="PROVIDER_${provider}_KEY_ENV"

    printf '%s' "${!variable:-}"
}

provider_api_key() {
    local provider="$1"

    local key_env
    key_env="$(provider_key_env "$provider")"

    # Local providers such as Ollama normally require no key.
    if [[ -z "$key_env" ]]; then
        printf ''
        return 0
    fi

    local key="${!key_env:-}"

    [[ -n "$key" ]] ||
        die "Provider '$provider' requires environment variable '$key_env'."

    printf '%s' "$key"
}

provider_check() {
    local provider="$1"

    local url
    url="$(provider_url "$provider")"

    [[ -n "$url" ]] ||
        die "Provider '$provider' has no URL."

    local key
    key="$(provider_api_key "$provider")"

    local -a headers

    headers=(
        -H "Content-Type: application/json"
    )

    if [[ -n "$key" ]]; then
        headers+=(
            -H "Authorization: Bearer $key"
        )
    fi

    curl \
        --fail \
        --silent \
        --show-error \
        --connect-timeout 10 \
        --max-time 20 \
        "${headers[@]}" \
        "${url%/}/models" \
        >/dev/null
}

# ============================================================
# API call
# ============================================================

api_call() {

    local provider="$1"
    local model="$2"
    local system_prompt="$3"
    local user_prompt="$4"
    local output="$5"

    local url
    url="$(provider_url "$provider")"

    [[ -n "$url" ]] ||
        die "Unknown provider: $provider"

    local key
    key="$(provider_api_key "$provider")"

    local payload

    payload="$(
        jq -n \
            --arg model "$model" \
            --arg system "$system_prompt" \
            --arg user "$user_prompt" \
            '{
                model: $model,
                messages: [
                    {
                        role: "system",
                        content: $system
                    },
                    {
                        role: "user",
                        content: $user
                    }
                ],
                temperature: 0.2
            }'
    )"

    local attempt=1

    while (( attempt <= MAX_API_RETRIES )); do

        local json="${output}.json"

        log "API: provider=$provider model=$model attempt=$attempt"

        local -a headers

        headers=(
            -H "Content-Type: application/json"
        )

        if [[ -n "$key" ]]; then
            headers+=(
                -H "Authorization: Bearer $key"
            )
        fi

        # OpenRouter-specific attribution headers.
        if [[ "$provider" == "openrouter" ]]; then
            headers+=(
                -H "HTTP-Referer: https://github.com/riscvcxh1/xh1-research"
                -H "X-Title: XH-1 Research Harness"
            )
        fi

        if curl \
            --fail \
            --silent \
            --show-error \
            --connect-timeout 30 \
            --max-time 1800 \
            "${headers[@]}" \
            "${url%/}/chat/completions" \
            -d "$payload" \
            > "$json"
        then

            if jq -er \
                '.choices[0].message.content' \
                "$json" \
                > "$output"
            then

                log "Response received: $(wc -c < "$output" | tr -d ' ') bytes"

                return 0
            fi

            log "Provider returned an invalid response."
        else
            log "API request failed."
        fi

        sleep $((attempt * attempt))

        ((attempt++))
    done

    return 1
}

# ============================================================
# Run state
# ============================================================

start_run() {

    RUN_ID="$(timestamp)"

    mkdir -p "$STATE/runs/$RUN_ID"

    : > "$STATE/runs/$RUN_ID/completed"
    : > "$STATE/runs/$RUN_ID/failed"
    : > "$STATE/runs/$RUN_ID/attempts"

    log "Started run: $RUN_ID"
}

run_has_completed() {
    grep -Fxq "$1" \
        "$STATE/runs/$RUN_ID/completed" \
        2>/dev/null
}

run_has_failed() {
    grep -Fxq "$1" \
        "$STATE/runs/$RUN_ID/failed" \
        2>/dev/null
}

mark_completed() {
    printf '%s\n' "$1" \
        >> "$STATE/runs/$RUN_ID/completed"
}

mark_failed() {
    printf '%s\n' "$1" \
        >> "$STATE/runs/$RUN_ID/failed"
}

record_attempt() {

    printf '%s\t%s\t%s\t%s\t%s\n' \
        "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \
        "$1" \
        "$2" \
        "$3" \
        "$4" \
        >> "$STATE/runs/$RUN_ID/attempts"
}

# ============================================================
# Research discovery
# ============================================================

is_soon() {
    grep -qE '^SOON[[:space:]]*$' "$1"
}

find_next_task() {

    while IFS= read -r -d '' file; do

        [[ "$file" == "$STATE"/* ]] && continue

        if is_soon "$file" &&
           ! run_has_completed "$file" &&
           ! run_has_failed "$file"
        then
            printf '%s\n' "$file"
            return 0
        fi

    done < <(
        find "$ROOT" \
            -type f \
            -name '*.md' \
            -not -path "$STATE/*" \
            -print0
    )

    return 1
}

# ============================================================
# Context
# ============================================================

build_context() {

    local file="$1"

    local relative="${file#"$ROOT"/}"
    local area="${relative%%/*}"

    cat <<EOF
XH-1 RESEARCH PROJECT
=====================

Project:
XH-1

Architecture:
Custom 128-core RISC-V processor

Repository:
XH-1 Research

Current document:
$file

Research area:
$area

CURRENT DOCUMENT
================

$(cat "$file")

RELATED DOCUMENTS
=================

EOF

    if [[ -d "$ROOT/$area" ]]; then

        find "$ROOT/$area" \
            -maxdepth 1 \
            -type f \
            -name '*.md' \
            ! -path "$file" |
            sort |
            head -20 |
            while read -r related; do

                echo
                echo "----- $related -----"
                cat "$related"

            done
    fi
}

# ============================================================
# Research prompts
# ============================================================

research_system_prompt() {

cat <<'EOF'
You are a senior computer architecture researcher working
on the XH-1 processor project.

XH-1 is a custom 128-core RISC-V CPU.

Produce rigorous engineering research.

Never invent:
- citations
- papers
- measurements
- benchmarks
- processor capabilities
- URLs
- experimental results

Clearly distinguish:

FACT
ASSUMPTION
PROPOSAL
RECOMMENDATION
OPEN QUESTION

If evidence is unavailable, say:

INSUFFICIENT EVIDENCE

Quantitative claims must have an identifiable source or must
be explicitly labeled as estimates.

Always consider:

- latency
- throughput
- area
- power
- energy
- bandwidth
- scalability
- contention
- implementation complexity
- verification complexity
- software implications
- 128-core replication

Do not assume XH-1 is in-order or out-of-order unless the
repository context establishes that fact.

Return ONLY the Markdown research document.
EOF
}

research_user_prompt() {

    local file="$1"

    cat <<EOF
Research the following XH-1 topic.

$(build_context "$file")

Produce a complete engineering research document.

Use sections where applicable:

# Topic

## Status

## Abstract

## Research Question

## Background

## Existing Approaches

## Alternative Designs

## Comparison

## Advantages

## Disadvantages

## XH-1 Considerations

## 128-Core Scalability

## Performance Considerations

## Area Considerations

## Power and Energy Considerations

## Implementation Considerations

## Verification Considerations

## Software Considerations

## Recommendation

## Confidence

## Open Questions

## Sources

Do not force a recommendation if evidence is insufficient.
EOF
}

# ============================================================
# Reviewer
# ============================================================

review_system_prompt() {

cat <<'EOF'
You are the independent technical reviewer for XH-1.

XH-1 is a custom 128-core RISC-V processor.

Aggressively check research for:

- factual errors
- incorrect RISC-V information
- unsupported claims
- fabricated citations
- fabricated measurements
- contradictory recommendations
- missing alternatives
- missing assumptions
- failure to consider 128-core scaling
- unrealistic implementation claims
- unsupported performance claims
- unsupported area claims
- unsupported power claims
- weak verification reasoning
- incorrect terminology
- insufficient source specificity
- assumptions presented as facts

Check quantitative claims especially carefully.

Check whether recommendations actually follow from
the analysis.

Return exactly:

VERDICT: PASS

or:

VERDICT: FAIL

Then:

ISSUES:
- ...

REQUIRED_FIXES:
- ...

CONFIDENCE: HIGH/MEDIUM/LOW
EOF
}

review_user_prompt() {

    local document="$1"

    cat <<EOF
Review this XH-1 research document independently.

DOCUMENT
========

$(cat "$document")

END DOCUMENT

Identify every substantive technical problem.

Do not rewrite the document.

Return PASS only when the research is technically acceptable,
internally consistent, and sufficiently supported.
EOF
}

# ============================================================
# Revision
# ============================================================

revision_system_prompt() {

cat <<'EOF'
You are a senior computer architecture researcher revising
research for the XH-1 128-core RISC-V CPU.

The previous version was rejected by an independent reviewer.

Fix every substantive reviewer issue.

You must:

- correct factual errors
- remove unsupported quantitative claims
- add missing alternatives
- reconcile contradictions
- make assumptions explicit
- improve citations
- reconsider unsupported recommendations
- consider 128-core scaling
- consider area
- consider power
- consider energy
- consider implementation
- consider verification

Never invent evidence.

If evidence cannot be established, write:

INSUFFICIENT EVIDENCE

Do not merely respond to the reviewer.

Produce a clean replacement document.

Return ONLY Markdown.
EOF
}

revision_user_prompt() {

    local document="$1"
    local review="$2"

    cat <<EOF
Revise this XH-1 research document.

ORIGINAL
========

$(cat "$document")

REVIEW
======

$(cat "$review")

Fix every substantive issue.

Do not add a response-to-reviewer section.

Return the complete corrected Markdown document only.
EOF
}

# ============================================================
# Replace SOON
# ============================================================

replace_soon() {

    local target="$1"
    local replacement="$2"

    is_soon "$target" ||
        die "Safety check failed: $target is no longer SOON."

    cp "$replacement" "$target"
}

# ============================================================
# Git
# ============================================================

git_commit() {

    [[ "$AUTO_COMMIT" == "true" ]] || return 0

    git rev-parse --is-inside-work-tree \
        >/dev/null 2>&1 ||
        return 0

    git add "$1"

    git commit \
        -m "research: $(basename "$1" .md | tr '-' ' ')" ||
        true
}

# ============================================================
# Research one topic
# ============================================================

research_topic() {

    local file="$1"

    log "=================================================="
    log "Researching: $file"
    log "=================================================="

    local round=1

    local current="$STATE/runs/$RUN_ID/${file//\//_}.current.md"
    local review="$STATE/runs/$RUN_ID/${file//\//_}.review.md"

    while (( round <= MAX_RESEARCH_ROUNDS )); do

        log "Research round $round/$MAX_RESEARCH_ROUNDS"

        local candidate="$STATE/runs/$RUN_ID/${file//\//_}.round${round}.md"

        # ----------------------------------------------------
        # Research
        # ----------------------------------------------------

        if (( round == 1 )); then

            log "Running researcher."

            if ! api_call \
                "$RESEARCH_PROVIDER" \
                "$RESEARCH_MODEL" \
                "$(research_system_prompt)" \
                "$(research_user_prompt "$file")" \
                "$candidate"
            then

                record_attempt \
                    "$file" "$round" "research" "api-failure"

                mark_failed "$file"

                return 1
            fi

            record_attempt \
                "$file" "$round" "research" "completed"

        # ----------------------------------------------------
        # Revision
        # ----------------------------------------------------

        else

            log "Running revision agent."

            if ! api_call \
                "$REVISION_PROVIDER" \
                "$REVISION_MODEL" \
                "$(revision_system_prompt)" \
                "$(revision_user_prompt "$current" "$review")" \
                "$candidate"
            then

                record_attempt \
                    "$file" "$round" "revision" "api-failure"

                mark_failed "$file"

                return 1
            fi

            record_attempt \
                "$file" "$round" "revision" "completed"
        fi

        cp "$candidate" "$current"

        # ----------------------------------------------------
        # Review
        # ----------------------------------------------------

        if [[ "$REVIEW_ENABLED" != "true" ]]; then

            replace_soon "$file" "$candidate"

            mark_completed "$file"

            git_commit "$file"

            return 0
        fi

        log "Running reviewer."

        if ! api_call \
            "$REVIEW_PROVIDER" \
            "$REVIEW_MODEL" \
            "$(review_system_prompt)" \
            "$(review_user_prompt "$candidate")" \
            "$review"
        then

            record_attempt \
                "$file" "$round" "review" "api-failure"

            mark_failed "$file"

            return 1
        fi

        echo
        echo "=========================================="
        echo "RESEARCH REVIEW"
        echo "=========================================="

        cat "$review"

        echo
        echo "=========================================="
        echo

        local verdict

        verdict="$(
            grep -m1 '^VERDICT:' "$review" || true
        )"

        record_attempt \
            "$file" "$round" "review" "$verdict"

        # ----------------------------------------------------
        # PASS
        # ----------------------------------------------------

        if [[ "$verdict" == "VERDICT: PASS" ]]; then

            log "Research PASSED review."

            replace_soon "$file" "$candidate"

            mark_completed "$file"

            git_commit "$file"

            log "Completed: $file"

            return 0
        fi

        # ----------------------------------------------------
        # FAIL
        # ----------------------------------------------------

        log "Research rejected by reviewer."

        if (( round >= MAX_RESEARCH_ROUNDS )); then

            log "Maximum research rounds reached."
            log "Leaving original document unchanged."
            log "Marking topic failed for this run."

            mark_failed "$file"

            return 2
        fi

        log "Preparing revision round $((round + 1))."

        ((round++))

        sleep "$DELAY_SECONDS"
    done

    mark_failed "$file"

    return 2
}

# ============================================================
# Run one
# ============================================================

run_single() {

    start_run

    local file="${1:-}"

    if [[ -z "$file" ]]; then
        file="$(find_next_task || true)"
    fi

    [[ -n "$file" ]] ||
        die "No research topics remain."

    research_topic "$file"
}

# ============================================================
# Run all
# ============================================================

run_all() {

    start_run

    local iteration=0

    while true; do

        if (( MAX_ITERATIONS > 0 &&
              iteration >= MAX_ITERATIONS )); then

            log "Maximum run iterations reached."

            break
        fi

        local file

        file="$(find_next_task || true)"

        if [[ -z "$file" ]]; then

            log "No remaining research topics."

            break
        fi

        ((iteration += 1))

        echo
        echo "##################################################"
        echo "XH-1 AUTONOMOUS RESEARCH"
        echo "Iteration: $iteration"
        echo "Topic: $file"
        echo "##################################################"
        echo

        research_topic "$file" || true

        sleep "$DELAY_SECONDS"
    done

    status
}

# ============================================================
# Status
# ============================================================

status() {

    local total
    local soon

    total="$(
        find "$ROOT" \
            -type f \
            -name '*.md' \
            -not -path "$STATE/*" |
        wc -l |
        tr -d ' '
    )"

    soon="$(
        grep -RIl \
            '^SOON[[:space:]]*$' \
            "$ROOT" \
            --include='*.md' \
            --exclude-dir='.xh1' \
            2>/dev/null |
        wc -l |
        tr -d ' '
    )"

    local completed=$((total - soon))

    echo
    echo "╔══════════════════════════════════════════╗"
    echo "║          XH-1 RESEARCH STATUS            ║"
    echo "╚══════════════════════════════════════════╝"
    echo

    printf "  Total documents : %s\n" "$total"
    printf "  Completed       : %s\n" "$completed"
    printf "  Remaining SOON  : %s\n" "$soon"

    if (( total > 0 )); then
        awk \
            -v done="$completed" \
            -v total="$total" \
            'BEGIN {
                printf "  Progress        : %.1f%%\n",
                (done / total) * 100
            }'
    fi

    echo
    echo "Next topic:"

    find_next_task || echo "  None"

    echo
}

# ============================================================
# Reset failure
# ============================================================

reset_failure() {

    local file="${1:-}"

    [[ -n "$file" ]] ||
        die "Usage: $0 reset-failure <file>"

    [[ -f "$file" ]] ||
        die "File does not exist: $file"

    if [[ -n "$RUN_ID" &&
          -f "$STATE/runs/$RUN_ID/failed" ]]; then

        sed -i "\|^${file}$|d" \
            "$STATE/runs/$RUN_ID/failed"
    fi

    log "Failure reset: $file"
}

# ============================================================
# Manual review
# ============================================================

review_command() {

    local file="${1:-}"

    [[ -n "$file" ]] ||
        die "Usage: $0 review <file>"

    [[ -f "$file" ]] ||
        die "File does not exist: $file"

    local output="$STATE/manual-review-$(timestamp).md"

    api_call \
        "$REVIEW_PROVIDER" \
        "$REVIEW_MODEL" \
        "$(review_system_prompt)" \
        "$(review_user_prompt "$file")" \
        "$output"

    cat "$output"
}

# ============================================================
# Provider status
# ============================================================

providers() {

    echo
    echo "XH-1 PROVIDERS"
    echo "=============="
    echo

    local provider

    for provider in $PROVIDERS; do

        local url
        url="$(provider_url "$provider")"

        printf "%-15s %s" "$provider" "$url"

        if provider_check "$provider" >/dev/null 2>&1; then
            echo "  [OK]"
        else
            echo "  [OFFLINE/ERROR]"
        fi

    done

    echo
}

# ============================================================
# Help
# ============================================================

help() {

cat <<'HELP'

XH-1 RESEARCH HARNESS

MULTI-PROVIDER AUTONOMOUS RESEARCH SYSTEM


COMMANDS
--------

  init

      Create a configuration template.

  status

      Show research progress.

  providers

      Test configured API providers.

  next

      Show next SOON document.

  run

      Research the next topic.

  run <file>

      Research a specific topic.

  run-all

      Process all SOON documents.

  review <file>

      Manually review a document.

  reset-failure <file>

      Retry a failed topic.

  help

      Show this help.


PIPELINE
--------

  RESEARCH
      |
      v
  REVIEW
      |
   +--+--+
   |     |
 PASS   FAIL
   |     |
   v     v
 WRITE  REVISE
          |
          v
        REVIEW


PROVIDERS
---------

Each agent can use a different provider.

Example:

  Researcher -> OpenRouter
  Reviewer   -> Ollama
  Revision   -> OpenRouter


SUPPORTED PROVIDER STYLE
------------------------

Any OpenAI-compatible API.

Examples:

  OpenRouter
  Ollama
  LM Studio
  vLLM
  LocalAI
  OpenAI
  Other compatible APIs


CONFIGURATION
-------------

PROVIDERS

  Space-separated provider names.

  Example:

      PROVIDERS="openrouter ollama"


Provider URL:

  PROVIDER_<name>_URL


Provider API key environment variable:

  PROVIDER_<name>_KEY_ENV


AGENTS

  RESEARCH_PROVIDER
  RESEARCH_MODEL

  REVIEW_PROVIDER
  REVIEW_MODEL

  REVISION_PROVIDER
  REVISION_MODEL


RESEARCH SETTINGS

  MAX_RESEARCH_ROUNDS
  MAX_API_RETRIES
  DELAY_SECONDS
  MAX_ITERATIONS
  REVIEW_ENABLED
  AUTO_COMMIT


EXAMPLE
-------

  export OPENROUTER_API_KEY="..."

  ./xh1-research providers

  ./xh1-research status

  ./xh1-research run

  ./xh1-research run-all

HELP
}

# ============================================================
# Main
# ============================================================

load_config
require_dependencies

case "${1:-status}" in

    init)
        echo "Edit $CONFIG to configure providers."
        ;;

    status)
        status
        ;;

    providers)
        providers
        ;;

    next)
        find_next_task || true
        ;;

    run)
        run_single "${2:-}"
        ;;

    run-all|loop|resume)
        run_all
        ;;

    review)
        review_command "${2:-}"
        ;;

    reset-failure)
        reset_failure "${2:-}"
        ;;

    help|-h|--help)
        help
        ;;

    *)
        die "Unknown command: $1"
        ;;

esac
