//! HTTP scrape API library; optional `scrape` module for Dexter Athena pipelines.

pub mod api;
pub mod cache;
pub mod config;
pub mod data;
pub mod monitoring;
pub mod proxy;
pub mod request;
pub mod scrape;

use actix_cors::Cors;
use actix_web::{web, App, HttpServer};
use athena_rs::AthenaClient;
use colored::Colorize;
use moka::future::Cache;
use prometheus::Registry;
use redis::Client;
use std::collections::HashMap;
use std::env::var;
use std::sync::Arc;

use crate::cache::BloomFilterCache;
use crate::config::load_scraper_configs;
use crate::config::ScraperConfig;
use crate::monitoring::metrics::{metrics_handler, AppMetrics};

pub struct AppState {
    pub athena: Option<Arc<AthenaClient>>,
    pub moka: Arc<Cache<String, String>>,
    pub prometheus_registry: Arc<Registry>,
    pub scraper_configs: Arc<HashMap<String, ScraperConfig>>,
    pub metrics: Arc<AppMetrics>,
    pub bloom: Arc<BloomFilterCache>,
    pub redis_client: Option<Arc<Client>>,
}

/// Run the dexter-sentinel HTTP server (Actix). Loads `.env` if present.
pub async fn run_server() -> std::io::Result<()> {
    dotenv::dotenv().ok();

    tracing_subscriber::fmt()
        .with_ansi(true)
        .with_env_filter(
            tracing_subscriber::EnvFilter::try_from_default_env()
                .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")),
        )
        .init();

    print_banner();

    tracing::info!(
        "{} {}",
        "→".bright_cyan(),
        "Loading scraper configurations...".bright_white()
    );

    let scraper_configs: HashMap<String, config::ScraperConfig> =
        load_scraper_configs("config/scrapers").unwrap_or_else(|e| {
            tracing::warn!("Failed to load scraper configs: {}", e);
            HashMap::new()
        });

    tracing::info!(
        "{} Loaded {} scraper configuration(s)",
        "✓".bright_green(),
        scraper_configs.len()
    );

    let athena: Option<Arc<AthenaClient>> = data::Provider::new().await.map(|p| p.client);
    if athena.is_some() {
        tracing::info!("{} Athena DB connected", "✓".bright_green());
    } else {
        tracing::warn!(
            "{} Athena not configured (set ATHENA_URL and ATHENA_API_KEY)",
            "⚠".bright_yellow()
        );
    }

    let redis_client: Option<Arc<Client>> = var("REDIS_URL")
        .ok()
        .and_then(|url| Client::open(url).ok())
        .map(Arc::new);

    if redis_client.is_some() {
        tracing::info!("{} Redis connected", "✓".bright_green());
    } else {
        tracing::warn!(
            "{} Redis not configured (REDIS_URL missing)",
            "⚠".bright_yellow()
        );
    }

    let moka: Arc<Cache<String, String>> = Arc::new(Cache::new(10_000));
    let prometheus_registry: Arc<Registry> = Arc::new(Registry::new());
    let metrics: Arc<AppMetrics> =
        Arc::new(AppMetrics::new(&prometheus_registry).expect("Failed to init metrics"));
    let bloom: Arc<BloomFilterCache> =
        Arc::new(BloomFilterCache::new(1_000_000, 0.01).expect("valid bloom filter params"));

    let state: web::Data<AppState> = web::Data::new(AppState {
        athena,
        moka,
        prometheus_registry: prometheus_registry.clone(),
        scraper_configs: Arc::new(scraper_configs),
        metrics,
        bloom,
        redis_client,
    });

    let registry_data: web::Data<Arc<Registry>> = web::Data::new(prometheus_registry);

    let host: String = var("HOST").unwrap_or_else(|_| "0.0.0.0".to_string());
    let port: u16 = var("PORT")
        .ok()
        .and_then(|p| p.parse().ok())
        .unwrap_or(8080);

    tracing::info!(
        "{} Server starting on {}:{}",
        "→".bright_cyan(),
        host.bright_white(),
        port.to_string().bright_yellow()
    );

    HttpServer::new(move || {
        let cors = Cors::permissive();
        App::new()
            .wrap(cors)
            .app_data(state.clone())
            .app_data(registry_data.clone())
            .route("/health", web::get().to(api::health::health_handler))
            .route("/metrics", web::get().to(metrics_handler))
            .route("/scrape", web::post().to(api::scrape::scrape_post_handler))
            .route(
                "/scrape/{config_name}",
                web::get().to(api::scrape::scrape_get_handler),
            )
            .route(
                "/api/cache/registry/add",
                web::post().to(api::cache::registry::add_url_handler),
            )
            .route(
                "/api/cache/registry/check",
                web::get().to(api::cache::registry::check_url_handler),
            )
    })
    .bind((host.as_str(), port))?
    .run()
    .await
}

fn print_banner() {
    let ver = env!("CARGO_PKG_VERSION");
    println!();
    println!(
        "  {}  {}",
        "dexter-sentinel".bright_cyan().bold(),
        format!("v{}", ver).bright_yellow()
    );
    println!(
        "  {}\n",
        "Config-driven HTTP scrape API · Actix Web · Prometheus".bright_white()
    );
}
