# justfile — libnovel-v2 task runner
# Install just: https://just.systems

scraper_dir  := "scraper"
ui_dir       := "ui"
ios_dir      := "ios/LibNovel"
ios_scheme   := "LibNovel"
ios_sim      := "platform=iOS Simulator,name=iPhone 17"
ios_spm      := ".spm-cache"
runner_temp  := env_var_or_default("RUNNER_TEMP", "/tmp")

# ─── Build ────────────────────────────────────────────────────────────────────

# Build the scraper binary
build:
    cd {{scraper_dir}} && go build -o bin/scraper ./cmd/scraper

# Build and verify all Go packages compile cleanly
build-all:
    cd {{scraper_dir}} && go build ./...

# ─── Tests ────────────────────────────────────────────────────────────────────

# Run unit tests only (no integration services required)
test:
    cd {{scraper_dir}} && go test -race -count=1 -timeout=60s ./...

# Run integration tests (requires MinIO, PocketBase, optional Browserless)
# Override env vars as needed, e.g.:
#   just test-integration MINIO_ENDPOINT=localhost:9000
test-integration:
    cd {{scraper_dir}} && go test -v -tags integration -timeout 600s ./...

# Run unit + integration tests
test-all: test test-integration

# Run a specific package's integration tests, e.g.:
#   just test-pkg internal/storage
test-pkg pkg:
    cd {{scraper_dir}} && go test -v -tags integration -timeout 600s ./{{pkg}}/...

# Run end-to-end tests against live services.
# All services must be running first (docker compose up -d or just e2e-up).
# Override env vars as needed, e.g.:
#   just test-e2e SCRAPER_URL=http://localhost:8080 KOKORO_VOICE=af_bella
test-e2e \
    browserless_url="http://localhost:3030" \
    minio_endpoint="localhost:9000" \
    pocketbase_url="http://localhost:8090" \
    scraper_url="http://localhost:8080":
    cd {{scraper_dir}} && \
        BROWSERLESS_URL={{browserless_url}} \
        MINIO_ENDPOINT={{minio_endpoint}} \
        POCKETBASE_URL={{pocketbase_url}} \
        SCRAPER_URL={{scraper_url}} \
        go test -v -tags integration -timeout 900s ./internal/e2e/...

# Start all services required for e2e tests, then run them
e2e: up test-e2e

# ─── Code quality ─────────────────────────────────────────────────────────────

# Run go vet on all packages (including integration build tag)
lint:
    cd {{scraper_dir}} && go vet ./...
    cd {{scraper_dir}} && go vet -tags integration ./...

# ─── UI ───────────────────────────────────────────────────────────────────────

# Type-check the SvelteKit UI
ui-check:
    cd {{ui_dir}} && npx svelte-check

# Start the SvelteKit dev server
ui-dev:
    cd {{ui_dir}} && npm run dev

# Install UI dependencies
ui-install:
    cd {{ui_dir}} && npm install

# Build the UI for production
ui-build:
    cd {{ui_dir}} && npm run build

# ─── iOS ──────────────────────────────────────────────────────────────────────

# Regenerate LibNovel.xcodeproj from project.yml (run after structural changes)
ios-gen:
    cd {{ios_dir}} && xcodegen generate --spec project.yml --project .

# Resolve SPM package dependencies (cached to {{ios_spm}})
ios-resolve:
    cd {{ios_dir}} && xcodebuild \
        -project {{ios_scheme}}.xcodeproj \
        -scheme {{ios_scheme}} \
        -resolvePackageDependencies \
        -clonedSourcePackagesDirPath {{ios_spm}}

# Build the iOS app for the simulator (no signing required)
# Runs ios-gen first to ensure the project is up to date.
ios-build: ios-gen ios-resolve
    cd {{ios_dir}} && set -o pipefail && xcodebuild \
        -project {{ios_scheme}}.xcodeproj \
        -scheme {{ios_scheme}} \
        -configuration Debug \
        -destination 'generic/platform=iOS Simulator' \
        -clonedSourcePackagesDirPath {{ios_spm}} \
        CODE_SIGNING_ALLOWED=NO \
        | xcpretty || xcodebuild \
            -project {{ios_scheme}}.xcodeproj \
            -scheme {{ios_scheme}} \
            -configuration Debug \
            -destination 'generic/platform=iOS Simulator' \
            -clonedSourcePackagesDirPath {{ios_spm}} \
            CODE_SIGNING_ALLOWED=NO

# Run unit tests on the simulator
# Runs ios-gen first to ensure the project is up to date.
ios-test: ios-gen ios-resolve
    cd {{ios_dir}} && set -o pipefail && xcodebuild test \
        -project {{ios_scheme}}.xcodeproj \
        -scheme {{ios_scheme}} \
        -configuration Debug \
        -destination '{{ios_sim}}' \
        -clonedSourcePackagesDirPath {{ios_spm}} \
        CODE_SIGNING_ALLOWED=NO \
        | xcpretty --report junit --output test-results.xml || true

# Archive a signed Release build (requires valid signing identity in keychain).
# Output: {{runner_temp}}/LibNovel.xcarchive
# Typically called from CI after importing certificate + provisioning profile.
# Usage: just ios-archive <team-id> <profile-uuid>
ios-archive team_id profile_uuid: ios-gen ios-resolve
    cd {{ios_dir}} && xcodebuild archive \
        -project {{ios_scheme}}.xcodeproj \
        -scheme {{ios_scheme}} \
        -configuration Release \
        -destination 'generic/platform=iOS' \
        -clonedSourcePackagesDirPath {{ios_spm}} \
        -archivePath {{runner_temp}}/LibNovel.xcarchive \
        CODE_SIGN_STYLE=Manual \
        DEVELOPMENT_TEAM="{{team_id}}" \
        PROVISIONING_PROFILE="{{profile_uuid}}"

# Export an IPA from the archive produced by ios-archive.
# Requires ios/LibNovel/ExportOptions.plist.
# Output: {{runner_temp}}/ipa/LibNovel.ipa
ios-export:
    cd {{ios_dir}} && xcodebuild -exportArchive \
        -archivePath {{runner_temp}}/LibNovel.xcarchive \
        -exportPath {{runner_temp}}/ipa \
        -exportOptionsPlist ExportOptions.plist

# Set the build number (CFBundleVersion) in project.yml before archiving.
# Usage: just ios-set-build-number 42
ios-set-build-number number:
    cd {{ios_dir}} && sed -i '' \
        's/CURRENT_PROJECT_VERSION: .*/CURRENT_PROJECT_VERSION: {{number}}/' \
        project.yml

# Upload the exported IPA to TestFlight via App Store Connect API.
# Requires env vars: ASC_KEY_ID, ASC_ISSUER_ID, ASC_PRIVATE_KEY_PATH
# The private key (.p8 file) must be present at ASC_PRIVATE_KEY_PATH.
ios-upload:
    xcrun altool --upload-app \
        --type ios \
        --file {{runner_temp}}/ipa/LibNovel.ipa \
        --apiKey "$ASC_KEY_ID" \
        --apiIssuer "$ASC_ISSUER_ID"

# ─── Docker Compose ───────────────────────────────────────────────────────────

# Start all services (browserless, kokoro, scraper, minio, pocketbase)
up:
    docker compose up -d

# Stop all services
down:
    docker compose down

# Tail logs for all services
logs:
    docker compose logs -f

# Tail logs for a specific service, e.g.: just logs-service scraper
logs-service service:
    docker compose logs -f {{service}}

# Rebuild and restart a specific service
restart service:
    docker compose up -d --build {{service}}

# ─── Local dev: individual services ──────────────────────────────────────────

# Start only PocketBase (for local storage testing)
pb-up:
    docker compose up -d pocketbase

# Start only MinIO (for local storage testing)
minio-up:
    docker compose up -d minio

# Start only Browserless (for local scraping tests)
browserless-up:
    docker compose up -d browserless

# Start storage backends only (MinIO + PocketBase)
storage-up:
    docker compose up -d minio pocketbase

# ─── Convenience ─────────────────────────────────────────────────────────────

# Show status of all docker compose services
status:
    docker compose ps

# Remove all stopped containers and unused images
prune:
    docker compose down --remove-orphans
    docker image prune -f

# One-shot scrape of the full catalogue (requires services to be running)
scrape-run: build
    cd {{scraper_dir}} && ./bin/scraper run

# One-shot scrape of a single book URL, e.g.:
#   just scrape-book https://novelfire.net/book/my-novel
scrape-book url: build
    cd {{scraper_dir}} && ./bin/scraper run --url {{url}}

# Start the HTTP server
serve: build
    cd {{scraper_dir}} && ./bin/scraper serve
