//! Router construction functions with different middleware configurations.
//!
//! This module contains the various `create_router_*` functions that configure
//! axum routers with different combinations of middleware layers:
//! - Basic (no metering)
//! - With Meter (economic throttling)
//! - With Admission (PoW + Meter)
//! - With Circuit Breaker (full protection stack)

use axum::{
    routing::{get, post},
    Router,
};
use std::sync::Arc;
use tower_http::cors::{Any, CorsLayer};
use tower_http::trace::TraceLayer;
use utoipa::OpenApi;
use utoipa_swagger_ui::SwaggerUi;

use crate::handlers;
use crate::middleware::{
    AdmissionLayer, ApiKeyAuthConfig, ApiKeyAuthLayer, CircuitBreakerLayer, MeterLayer,
};
use crate::state::AppState;
use crate::ApiDoc;

/// Get the combined OpenAPI documentation.
///
/// When the `aphoria` feature is enabled, this merges the Aphoria endpoints
/// into the main API documentation.
fn openapi_doc() -> utoipa::openapi::OpenApi {
    #[cfg(feature = "aphoria")]
    {
        use crate::aphoria_openapi::AphoriaApiDoc;
        let mut doc = ApiDoc::openapi();
        let aphoria_doc = AphoriaApiDoc::openapi();

        // Merge paths
        for (path, item) in aphoria_doc.paths.paths {
            doc.paths.paths.insert(path, item);
        }

        // Merge schemas if aphoria has components
        if let Some(aphoria_components) = aphoria_doc.components {
            if let Some(ref mut doc_components) = doc.components {
                for (name, schema) in aphoria_components.schemas {
                    doc_components.schemas.insert(name, schema);
                }
            } else {
                doc.components = Some(aphoria_components);
            }
        }

        // Merge tags
        if let Some(ref mut tags) = doc.tags {
            if let Some(aphoria_tags) = aphoria_doc.tags {
                tags.extend(aphoria_tags);
            }
        } else {
            doc.tags = aphoria_doc.tags;
        }

        doc
    }

    #[cfg(not(feature = "aphoria"))]
    {
        ApiDoc::openapi()
    }
}

/// Create the axum router with all routes and OpenAPI documentation.
///
/// This creates a router without economic throttling (The Meter).
/// For production use, prefer `create_router_with_meter`.
pub fn create_router(state: AppState) -> Router {
    let cors = CorsLayer::new()
        .allow_origin(Any) // For development; restrict in production
        .allow_methods(Any)
        .allow_headers(Any);

    let api_router =
        build_api_routes().with_state(state).layer(TraceLayer::new_for_http()).layer(cors);

    Router::new()
        .merge(SwaggerUi::new("/swagger-ui").url("/api-docs/openapi.json", openapi_doc()))
        .merge(api_router)
}

/// Create the axum router with economic throttling (The Meter) enabled.
///
/// This router enforces per-agent per-hour quotas based on operation costs:
/// - Assert: 10 tokens base + 1/KB payload
/// - Vote: 1 token base + 1/KB payload
/// - Query: 5 tokens base + 1 per lens + 1/KB payload
///
/// Agents must provide `X-Agent-Id` header (hex-encoded Ed25519 public key).
/// Quota status headers are returned on all responses:
/// - `X-Quota-Remaining`: Tokens left in current window
/// - `X-Quota-Limit`: Total tokens per hour
/// - `X-Quota-Reset`: Unix timestamp when window resets
pub fn create_router_with_meter(state: AppState) -> Router {
    let cors = CorsLayer::new().allow_origin(Any).allow_methods(Any).allow_headers(Any);
    let meter_layer = MeterLayer::new(Arc::clone(&state.quota_store));

    let api_router = build_api_routes()
        .with_state(state)
        .layer(meter_layer)
        .layer(TraceLayer::new_for_http())
        .layer(cors);

    Router::new()
        .merge(SwaggerUi::new("/swagger-ui").url("/api-docs/openapi.json", openapi_doc()))
        .merge(api_router)
}

/// Create the axum router with full admission control enabled (The Shield + The Meter).
///
/// This router enforces both proof-of-work admission control AND economic throttling.
/// New/untrusted agents must solve PoW puzzles before their assertions are accepted,
/// and all agents are subject to quota limits based on their trust tier.
///
/// # Admission Control (The Shield)
///
/// - First 10 assertions: 16-bit PoW (~16 seconds to solve)
/// - Assertions 11-50: 1-bit PoW (trivial)
/// - 50+ assertions OR trust > 0.6: PoW exempt
///
/// # Trust Tiers
///
/// | Trust Range | Tier       | Quota Multiplier |
/// |-------------|------------|------------------|
/// | 0.0-0.3     | Untrusted  | 0.1x (1,000/hr)  |
/// | 0.3-0.5     | Limited    | 0.5x (5,000/hr)  |
/// | 0.5-0.7     | Verified   | 1.0x (10,000/hr) |
/// | 0.7-0.9     | Trusted    | 2.0x (20,000/hr) |
/// | 0.9-1.0     | Authority  | 10.0x (100k/hr)  |
///
/// # Headers
///
/// **Request headers:**
/// - `X-Agent-Id`: Agent's Ed25519 public key (hex, 64 chars)
/// - `X-PoW-Nonce`: PoW solution nonce (decimal, required if PoW needed)
/// - `X-PoW-Timestamp`: PoW timestamp (Unix seconds, required if PoW needed)
///
/// **Response headers:**
/// - `X-Trust-Tier`: Agent's trust tier name
/// - `X-PoW-Required`: "true" or "false"
/// - `X-PoW-Difficulty`: Required difficulty in bits
/// - `X-Quota-Remaining`: Tokens left in current window
/// - `X-Quota-Limit`: Total tokens per hour
/// - `X-Quota-Reset`: Unix timestamp when window resets
pub fn create_router_with_admission(state: AppState) -> Router {
    let cors = CorsLayer::new().allow_origin(Any).allow_methods(Any).allow_headers(Any);
    let admission_layer = AdmissionLayer::new(Arc::clone(&state.admission_store));
    let meter_layer = MeterLayer::new(Arc::clone(&state.quota_store));

    // Layer order: admission (outer) -> meter (inner)
    // This means: check PoW first, then check quota
    let api_router = build_api_routes()
        .with_state(state)
        .layer(meter_layer) // Inner: runs second (check quota)
        .layer(admission_layer) // Outer: runs first (check PoW)
        .layer(TraceLayer::new_for_http())
        .layer(cors);

    Router::new()
        .merge(SwaggerUi::new("/swagger-ui").url("/api-docs/openapi.json", openapi_doc()))
        .merge(api_router)
}

/// Create the axum router with API key authentication enabled.
///
/// This router enforces API key authentication for protected endpoints:
/// - Admin endpoints (`/v1/admin/*`): Require `Admin` role
/// - Write endpoints (`/v1/assert`, `/v1/vote`, `/v1/supersede`): Require `WriteAgent` or higher
/// - Read endpoints: Available to all authenticated users
///
/// # Default Behavior
///
/// By default, only `/v1/admin/*` endpoints require authentication.
/// Set `require_for_all: true` in config to require auth for all endpoints.
///
/// # Headers
///
/// **Request:**
/// - `X-API-Key`: The API key (format: `steme_{env}_{random_hex_48}`)
///
/// **Response:**
/// - `X-Rate-Limit-Remaining`: Per-key requests remaining
/// - `X-Rate-Limit-Reset`: Unix timestamp when window resets
///
/// # Bootstrap
///
/// On first start, set `STEMEDB_ROOT_API_KEY` environment variable to bootstrap
/// an admin key. This key will be hashed and stored, then can be used to create
/// additional keys via `POST /v1/admin/api-keys`.
pub fn create_router_with_auth(state: AppState) -> Router {
    create_router_with_auth_config(state, ApiKeyAuthConfig::default())
}

/// Create the axum router with API key authentication and custom config.
pub fn create_router_with_auth_config(state: AppState, auth_config: ApiKeyAuthConfig) -> Router {
    let cors = CorsLayer::new().allow_origin(Any).allow_methods(Any).allow_headers(Any);
    let api_key_layer = ApiKeyAuthLayer::with_config(Arc::clone(&state.api_key_store), auth_config);

    let api_router = build_api_routes()
        .with_state(state)
        .layer(api_key_layer)
        .layer(TraceLayer::new_for_http())
        .layer(cors);

    Router::new()
        .merge(SwaggerUi::new("/swagger-ui").url("/api-docs/openapi.json", openapi_doc()))
        .merge(api_router)
}

/// Create the axum router with all protection layers (Auth + Circuit Breaker + Admission + Meter).
///
/// This is the most secure configuration, combining:
/// 1. **API Key Auth** (outermost): Validates API key and role permissions
/// 2. **Circuit Breaker**: Blocks misbehaving agents
/// 3. **Admission Control**: Requires PoW for untrusted agents
/// 4. **Meter** (innermost): Enforces quota limits
pub fn create_router_full_protection(state: AppState) -> Router {
    create_router_full_protection_config(state, ApiKeyAuthConfig::default())
}

/// Create the fully protected router with custom auth config.
pub fn create_router_full_protection_config(
    state: AppState,
    auth_config: ApiKeyAuthConfig,
) -> Router {
    let cors = CorsLayer::new().allow_origin(Any).allow_methods(Any).allow_headers(Any);
    let api_key_layer = ApiKeyAuthLayer::with_config(Arc::clone(&state.api_key_store), auth_config);
    let circuit_breaker_layer = CircuitBreakerLayer::new(Arc::clone(&state.circuit_breaker_store));
    let admission_layer = AdmissionLayer::new(Arc::clone(&state.admission_store));
    let meter_layer = MeterLayer::new(Arc::clone(&state.quota_store));

    // Layer order: api_key (outer) -> circuit_breaker -> admission -> meter (inner)
    let api_router = build_api_routes()
        .with_state(state)
        .layer(meter_layer) // Inner: runs fourth (check quota)
        .layer(admission_layer) // Middle: runs third (check PoW)
        .layer(circuit_breaker_layer) // Middle: runs second (check circuit)
        .layer(api_key_layer) // Outer: runs FIRST (check API key)
        .layer(TraceLayer::new_for_http())
        .layer(cors);

    Router::new()
        .merge(SwaggerUi::new("/swagger-ui").url("/api-docs/openapi.json", openapi_doc()))
        .merge(api_router)
}

/// Create the axum router with full protection enabled (Circuit Breaker + Admission + Meter).
///
/// This router has all three defensive layers:
/// 1. **Circuit Breaker** (outermost): Blocks misbehaving agents before any processing
/// 2. **Admission Control**: Requires PoW for untrusted agents
/// 3. **Meter** (innermost): Enforces quota limits
///
/// # Layer Execution Order
///
/// ```text
/// Request → CircuitBreaker → Admission → Meter → Handler → Response
/// ```
///
/// # Circuit Breaker
///
/// Agents that repeatedly fail (invalid signatures, malformed input, PoW failures)
/// get their circuits tripped. Blocked agents receive 503 with Retry-After header.
///
/// - 5 failures within 60 seconds: Circuit trips (Open)
/// - 30 seconds in Open state: Transitions to HalfOpen (test)
/// - 1 success in HalfOpen: Circuit closes (back to normal)
/// - Failure in HalfOpen: Circuit trips again
///
/// # Response Headers (when blocked)
///
/// - `X-Circuit-Breaker-State`: "open" or "half_open"
/// - `X-Circuit-Breaker-Retry-After`: Seconds until retry
/// - `X-Circuit-Breaker-Failures`: Number of failures
/// - `Retry-After`: Standard HTTP header (seconds)
pub fn create_router_with_circuit_breaker(state: AppState) -> Router {
    let cors = CorsLayer::new().allow_origin(Any).allow_methods(Any).allow_headers(Any);
    let circuit_breaker_layer = CircuitBreakerLayer::new(Arc::clone(&state.circuit_breaker_store));
    let admission_layer = AdmissionLayer::new(Arc::clone(&state.admission_store));
    let meter_layer = MeterLayer::new(Arc::clone(&state.quota_store));

    // Layer order: circuit_breaker (outer) -> admission (middle) -> meter (inner)
    let api_router = build_api_routes()
        .with_state(state)
        .layer(meter_layer) // Inner: runs third (check quota)
        .layer(admission_layer) // Middle: runs second (check PoW)
        .layer(circuit_breaker_layer) // Outer: runs FIRST (check circuit)
        .layer(TraceLayer::new_for_http())
        .layer(cors);

    Router::new()
        .merge(SwaggerUi::new("/swagger-ui").url("/api-docs/openapi.json", openapi_doc()))
        .merge(api_router)
}

/// Build the API routes without state or layers.
///
/// This is an internal helper that defines all the routes and handlers.
fn build_api_routes() -> Router<AppState> {
    let router = Router::new()
        // Prometheus metrics endpoint (bypasses metering/admission)
        .route("/metrics", get(handlers::metrics_handler))
        .route("/v1/assert", post(handlers::create_assertion))
        .route("/v1/epoch", post(handlers::create_epoch))
        .route("/v1/vote", post(handlers::create_vote))
        .route("/v1/query", get(handlers::query_assertions))
        .route("/v1/skeptic", get(handlers::skeptic_query))
        .route("/v1/layered", get(handlers::layered_query))
        .route("/v1/constraints", get(handlers::constraints_query))
        .route("/health", get(handlers::health_check)) // Alias for dashboard
        .route("/v1/health", get(handlers::health_check))
        .route("/v1/audit/queries", get(handlers::list_audits))
        .route("/v1/audit/query/{id}", get(handlers::get_audit))
        .route("/v1/trace", get(handlers::trace))
        .route("/v1/supersede", post(handlers::supersede))
        .route("/v1/meter/quota", get(handlers::get_quota_status))
        .route("/v1/meter/quota/limit", post(handlers::set_quota_limit))
        .route("/v1/source", post(handlers::store_source))
        .route("/v1/provenance/{hash}", get(handlers::get_provenance))
        .route("/v1/admin/decay-trust-ranks", post(handlers::decay_trust_ranks))
        .route("/v1/admin/escalations", get(handlers::list_escalations))
        .route("/v1/admin/escalations/:id/resolve", post(handlers::resolve_escalation))
        .route("/v1/admin/gold-standards", post(handlers::create_gold_standard))
        .route("/v1/admin/gold-standards", get(handlers::list_gold_standards))
        .route(
            "/v1/admin/gold-standards/:subject/:predicate",
            axum::routing::delete(handlers::remove_gold_standard),
        )
        .route("/v1/admin/verify-agent", post(handlers::verify_agent))
        // Concept hierarchy and alias endpoints
        .route("/v1/concepts/alias", post(handlers::create_alias))
        .route("/v1/concepts/alias", axum::routing::delete(handlers::delete_alias))
        .route("/v1/concepts/resolve", get(handlers::resolve_alias))
        .route("/v1/concepts/aliases", get(handlers::list_aliases))
        .route("/v1/concepts/suggest", get(handlers::suggest_aliases))
        .route("/v1/concepts/parse", get(handlers::parse_concept_path))
        // Admission control endpoints
        .route("/v1/admission/status", get(handlers::get_admission_status))
        // Quarantine endpoints (Content Defense Phase 7C)
        .route("/v1/admin/quarantine", get(handlers::list_quarantine))
        .route("/v1/admin/quarantine/:hash", get(handlers::get_quarantine))
        .route("/v1/admin/quarantine/:hash/approve", post(handlers::approve_quarantine))
        .route("/v1/admin/quarantine/:hash/reject", post(handlers::reject_quarantine))
        // Circuit breaker endpoints (Phase 7D)
        .route("/v1/admin/circuit-breaker/:agent_id", get(handlers::get_circuit_status))
        .route("/v1/admin/circuit-breaker/reset", post(handlers::reset_circuit))
        .route("/v1/admin/circuit-breakers/tripped", get(handlers::list_tripped_circuits))
        // API key management endpoints (P4.2)
        .route("/v1/admin/api-keys", post(handlers::create_api_key))
        .route("/v1/admin/api-keys", get(handlers::list_api_keys))
        .route("/v1/admin/api-keys/:key_hash", axum::routing::delete(handlers::revoke_api_key))
        .route("/v1/admin/api-keys/:key_hash", axum::routing::patch(handlers::update_api_key))
        .route("/v1/admin/api-keys/:key_hash/rotate", post(handlers::rotate_api_key))
        // Source registry endpoints
        .route("/v1/sources", post(handlers::register_source))
        .route("/v1/sources", get(handlers::list_sources))
        .route("/v1/sources/:hash", get(handlers::get_source))
        .route("/v1/sources/:hash/status", axum::routing::patch(handlers::update_source_status))
        // Source impact analysis (P3.1)
        .route("/v1/sources/:hash/impact", get(handlers::get_source_impact))
        .route("/v1/sources/:hash/quarantine", post(handlers::quarantine_source))
        .route("/v1/sources/:hash/restore", post(handlers::restore_source))
        // Source impact export (P3.2)
        .route("/v1/sources/:hash/impact/export", get(handlers::export_source_impact));

    // Add Aphoria endpoints when feature is enabled
    #[cfg(feature = "aphoria")]
    {
        router
            .route("/v1/aphoria/bless", post(handlers::bless))
            .route("/v1/aphoria/policy/export", post(handlers::export_policy))
            .route("/v1/aphoria/policy/import", post(handlers::import_policy))
            .route("/v1/aphoria/scan", post(handlers::scan))
            .route("/v1/aphoria/scans", get(handlers::list_scans))
            .route("/v1/aphoria/observations", post(handlers::push_observations))
            // Community corpus endpoints
            .route(
                "/v1/aphoria/community/observations",
                post(handlers::push_community_observations),
            )
            .route("/v1/aphoria/patterns", get(handlers::get_patterns))
    }

    #[cfg(not(feature = "aphoria"))]
    {
        router
    }
}
