# MERIDIAN working root. Paths here are never prefixed with a parent folder.
# Run every recipe from this directory (CWD = this Justfile).
set dotenv-load := false
set positional-arguments := true

export COMPOSE_ENV_FILES := ".env,.env.local"

desktop_dir := "meridian-desktop"
web_dir := "meridian-web"
admin_dir := "meridian-admin-web"
mobile_dir := "meridian-mobile"
desktop_tauri_manifest := "meridian-desktop/src-tauri/Cargo.toml"

default:
    @just --list

# Inventory + leftover parent-path scan
doctor:
    ./doctor.sh

# Port-deconflicted launcher (services + cargo relay + desktop)
all:
    ./run.sh all

# Local cargo-relay backing stack (cicd/docker-compose.services.yml + profiles)
up *ARGS:
    ./run.sh up {{ ARGS }}

down *ARGS:
    ./run.sh down {{ ARGS }}

stop:
    ./run.sh stop

status:
    ./run.sh status

# Apply schema + seed loopback community hosts
migrate:
    ./run.sh up

# Cargo relay against the local backing stack
relay:
    ./run.sh relay

# Native desktop (starts services + relay, then tauri)
desktop:
    ./run.sh desktop

# Compile the five Tauri externalBin sidecars (debug). Replaces zero-byte stubs.
sidecar-build:
    cargo build -p meridian-acp -p meridian-agent -p meridian-dev-mcp -p git-credential-nostr -p meridian-cli

# Release-build those sidecars and copy them into meridian-desktop/src-tauri/binaries/
bundle-sidecars target="":
    #!/usr/bin/env bash
    set -euo pipefail
    TARGET="{{ target }}"
    if [[ -n "$TARGET" ]]; then
        cargo build --release --target "$TARGET" \
            -p meridian-acp -p meridian-agent -p meridian-dev-mcp \
            -p git-credential-nostr -p meridian-cli
        ./scripts/bundle-sidecars.sh "$TARGET"
    else
        cargo build --release \
            -p meridian-acp -p meridian-agent -p meridian-dev-mcp \
            -p git-credential-nostr -p meridian-cli
        ./scripts/bundle-sidecars.sh
    fi

# Hosted-community control plane (local replacement for the hosted one).
# Generates a relay-operator keypair on first run under .control-plane/.
# The relay must list that pubkey in RELAY_OPERATOR_PUBKEYS.
control-plane:
    #!/usr/bin/env bash
    set -euo pipefail
    cd {{ justfile_directory() }}
    if [[ -f .env ]]; then set -a; . ./.env; set +a; fi
    if [[ -f .env.local ]]; then set -a; . ./.env.local; set +a; fi

    # A configured secret wins over a generated one. Minting a fresh key while
    # .env already carries MERIDIAN_CONTROL_RELAY_OPERATOR_SECRET_KEY produces a
    # pubkey the running relay has never trusted, and the recipe then reports
    # that as the relay's fault.
    secret="${MERIDIAN_CONTROL_RELAY_OPERATOR_SECRET_KEY:-}"
    if [[ -z "$secret" ]]; then
        key_dir=".control-plane"
        key_file="$key_dir/operator.key"
        mkdir -p "$key_dir"
        if [[ ! -f "$key_file" ]]; then
            openssl rand -hex 32 > "$key_file"
            chmod 600 "$key_file"
            echo "Generated a new relay-operator key at $key_file"
        fi
        secret="$(tr -d '[:space:]' < "$key_file")"
    fi
    pubkey="$(MERIDIAN_CONTROL_RELAY_OPERATOR_SECRET_KEY="$secret" \
        cargo run --quiet -p meridian-control-plane -- --print-operator-pubkey)"
    if [[ -z "$pubkey" ]]; then
        echo "error: could not derive the operator pubkey from $key_file" >&2
        exit 1
    fi

    # Both layers, because .env.local is where a relocated port lands and it is
    # the layer that wins. Grepping only .env made a correct setup unrunnable.
    if ! grep -qs "^RELAY_OPERATOR_PUBKEYS=.*$pubkey" .env .env.local; then
        cat >&2 <<EOF

    The relay does not yet trust this control plane's operator key.
    Add these two lines to .env.local, restart the relay, then re-run:

      RELAY_OPERATOR_PUBKEYS=$pubkey
      RELAY_OPERATOR_API_ORIGIN=${MERIDIAN_CONTROL_RELAY_OPERATOR_API_ORIGIN:-http://127.0.0.1:3000}

    EOF
        exit 1
    fi

    export MERIDIAN_CONTROL_DATABASE_URL="${MERIDIAN_CONTROL_DATABASE_URL:-postgres://meridian:meridian_dev@localhost:5432/meridian_control_plane}"
    export MERIDIAN_CONTROL_PUBLIC_ORIGIN="${MERIDIAN_CONTROL_PUBLIC_ORIGIN:-http://127.0.0.1:8090}"
    export MERIDIAN_CONTROL_RELAY_OPERATOR_API_ORIGIN="${MERIDIAN_CONTROL_RELAY_OPERATOR_API_ORIGIN:-http://127.0.0.1:3000}"
    export MERIDIAN_CONTROL_RELAY_OPERATOR_SECRET_KEY="$secret"
    export MERIDIAN_CONTROL_COMMUNITY_HOST_SUFFIX="${MERIDIAN_CONTROL_COMMUNITY_HOST_SUFFIX:-relays.meridian.localtest.me}"
    export MERIDIAN_CONTROL_COMMUNITY_HOST_PORT="${MERIDIAN_CONTROL_COMMUNITY_HOST_PORT:-3000}"
    export MERIDIAN_CONTROL_RELAY_SCHEME="${MERIDIAN_CONTROL_RELAY_SCHEME:-ws}"
    export MERIDIAN_CONTROL_IDENTITY_PROVIDER="${MERIDIAN_CONTROL_IDENTITY_PROVIDER:-dev}"
    export MERIDIAN_CONTROL_ALLOW_DEV_LOGIN="${MERIDIAN_CONTROL_ALLOW_DEV_LOGIN:-1}"
    export MERIDIAN_CONTROL_RUNTIME_DATABASE_ROLE="${MERIDIAN_CONTROL_RUNTIME_DATABASE_ROLE:-meridian}"

    cargo run -p meridian-control-plane -- --migrate-only
    base_path="${MERIDIAN_CONTROL_API_BASE_PATH:-/api/meridian}"
    origin="${MERIDIAN_CONTROL_PUBLIC_ORIGIN:-http://127.0.0.1:8090}"
    echo ""
    echo "Control plane:  ${origin}${base_path}"
    echo "Operator key:   $pubkey"
    echo "Communities:    <name>.${MERIDIAN_CONTROL_COMMUNITY_HOST_SUFFIX}:${MERIDIAN_CONTROL_COMMUNITY_HOST_PORT}"
    echo ""
    cargo run -p meridian-control-plane

_ensure-sidecar-stubs:
    #!/usr/bin/env bash
    set -euo pipefail
    TARGET=$(rustc -vV | sed -n 's|host: ||p')
    mkdir -p meridian-desktop/src-tauri/binaries
    for bin in meridian-acp meridian-agent meridian-dev-mcp git-credential-nostr meridian; do
        touch "meridian-desktop/src-tauri/binaries/${bin}-${TARGET}"
    done

# Client workspace
install:
    pnpm install

desktop-dev:
    #!/usr/bin/env bash
    set -euo pipefail
    source scripts/instance-env.sh
    pnpm --dir {{ desktop_dir }} exec vite --port "${MERIDIAN_VITE_PORT}" --strictPort

desktop-check:
    pnpm --dir {{ desktop_dir }} check

desktop-typecheck:
    pnpm --dir {{ desktop_dir }} typecheck

desktop-test:
    pnpm --dir {{ desktop_dir }} test

desktop-build:
    pnpm --dir {{ desktop_dir }} build

web-dev:
    pnpm --dir {{ web_dir }} dev

web-check:
    pnpm --dir {{ web_dir }} check

web-build:
    pnpm --dir {{ web_dir }} build

admin-check:
    pnpm --dir {{ admin_dir }} check

admin-build:
    pnpm --dir {{ admin_dir }} build

mobile-check:
    #!/usr/bin/env bash
    set -euo pipefail
    cd {{ mobile_dir }}
    dart format --output=none --set-exit-if-changed .
    flutter analyze

mobile-test:
    #!/usr/bin/env bash
    set -euo pipefail
    cd {{ mobile_dir }}
    flutter test

desktop-fix:
    pnpm --dir {{ desktop_dir }} exec biome check --write .
    pnpm --dir {{ desktop_dir }} check:file-sizes

desktop-tauri-check: _ensure-sidecar-stubs
    cargo check --manifest-path {{ desktop_tauri_manifest }}

desktop-tauri-clippy: _ensure-sidecar-stubs
    cargo clippy --manifest-path {{ desktop_tauri_manifest }} --all-targets -- -D warnings

desktop-tauri-test: _ensure-sidecar-stubs
    cargo test --manifest-path {{ desktop_tauri_manifest }}

fmt:
    cargo fmt --all

fmt-check:
    cargo fmt --all -- --check

clippy:
    cargo clippy --workspace --all-targets -- -D warnings

# Lib tests only — no Postgres / Dragonfly / relay.
test-unit:
    cargo test --workspace --lib

# Unit + crate integration tests that do not need a live relay.
# meridian-test-client E2E stays #[ignore] — use test-e2e for those.
test:
    ./scripts/run-tests.sh all

test-integration:
    ./scripts/run-tests.sh integration

# Start backing services + a cargo relay, then run the ignored E2E suite.
test-e2e:
    ./scripts/start-relay-for-tests.sh --profile dev
    cargo test -p meridian-test-client -- --ignored --nocapture

# NIP-AB pairing interop binary (drives meridian-pair-relay).
pairing-cli:
    cargo build -p meridian-pairing-cli

# Multicall ACP + agent + dev-mcp binary.
harness-build:
    cargo build -p meridian-harness

# `docs/mips/README.md` is the one MIP register. The desktop capability view
# reads a generated copy; this fails when the two drift. Pass `--write` to
# regenerate after editing the register.
check-mips *ARGS:
    node scripts/check-mip-registry.mjs {{ ARGS }}
    node --test scripts/mip-registry-core.test.mjs

hooks:
    lefthook install

# Local gate this tree can actually run. Not the upstream `just ci`
# (unwrap/STELLAR/kinds/gauntlet live next door).
check: doctor fmt-check clippy desktop-check desktop-typecheck web-check admin-check mobile-check check-mips

ci: check test-unit desktop-test desktop-tauri-check

# IHLC compose stack (IronBank bases — fails off that network)
compose-config:
    # COMPOSE_FILE in cicd/.env.local is relative to CWD (IHLC stack)
    cd cicd && docker compose --env-file .env.local config

compose-up:
    cd cicd && docker compose --env-file .env.local up -d --build

compose-down:
    cd cicd && docker compose --env-file .env.local down

# Cargo-relay backing stack. Up/down go through ./run.sh so the host ports come
# from .settings/run-profiles/<profile>.yaml, the same source the relay's
# DATABASE_URL / REDIS_URL / MERIDIAN_S3_ENDPOINT are derived from. Calling
# compose directly here published cicd/.env.local's stock 5432/6379/9000 while
# the relay dialled the relocated ports — every container healthy, every
# connection refused.
compose-services-up:
    ./run.sh up

compose-services-down:
    ./run.sh down

# Parse check only. Renders cicd/.env.local's stock ports because run.sh is not
# in the path to export the resolved ones; that is fine for validating the file
# and wrong to read as the allocation.
compose-services-config:
    cd cicd && COMPOSE_FILE=docker-compose.services.yml docker compose --env-file .env.local config

# Helm charts under cicd/charts/ (copy; IHLC still deploys via scp + compose).
# Bare lint fails schema: charts refuse empty required values. Use the
# fixtures the contract already ships.
helm-lint:
    helm lint cicd/charts/meridian -f cicd/charts/meridian/ci/quickstart-values.yaml
    helm lint cicd/charts/meridian-control-plane \
        --set 'oidc.issuer=https://login.example.com/realms/meridian' \
        --set 'image.digest=sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'

helm-template:
    helm template meridian cicd/charts/meridian -f cicd/charts/meridian/ci/quickstart-values.yaml
    helm template control-plane cicd/charts/meridian-control-plane \
        --set 'oidc.issuer=https://login.example.com/realms/meridian' \
        --set 'image.digest=sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'

helm-contract:
    bash cicd/charts/meridian-control-plane/tests/render.sh
