use std::sync::{Arc, Mutex};

use axum::routing::{get, post};
use axum::Router;
use rusqlite::Connection;
use tower_http::cors::CorsLayer;

use crate::handlers;
use crate::rate_limit::RateLimiter;

pub struct AppState {
    pub db: Mutex<Connection>,
    pub notify_email: String,
    pub mail_domain: String,
    pub qa_reply_domain: String,
    pub rate_limiter: RateLimiter,
    pub webhook_secret: String,
}

pub async fn run() -> Result<(), Box<dyn std::error::Error>> {
    let db_path = std::env::var("QA_DB_PATH").unwrap_or_else(|_| "qa.db".to_string());
    let notify_email = std::env::var("QA_NOTIFY_EMAIL").expect("QA_NOTIFY_EMAIL must be set");
    let mail_domain =
        std::env::var("QA_MAIL_DOMAIN").unwrap_or_else(|_| "extremist.software".to_string());
    let qa_reply_domain =
        std::env::var("QA_REPLY_DOMAIN").unwrap_or_else(|_| mail_domain.clone());
    let webhook_secret = std::env::var("WEBHOOK_SECRET").expect("WEBHOOK_SECRET must be set");

    let conn = Connection::open(&db_path)?;
    conn.execute_batch("PRAGMA journal_mode=WAL;")?;
    conn.execute_batch(
        "CREATE TABLE IF NOT EXISTS questions (
            id          INTEGER PRIMARY KEY AUTOINCREMENT,
            question    TEXT    NOT NULL,
            answer      TEXT,
            created_at  TEXT    NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
            answered_at TEXT
        );",
    )?;

    let state = Arc::new(AppState {
        db: Mutex::new(conn),
        notify_email,
        mail_domain,
        qa_reply_domain,
        rate_limiter: RateLimiter::new(5, 3600),
        webhook_secret,
    });

    let app = Router::new()
        .route(
            "/api/questions",
            get(handlers::get_questions).post(handlers::post_question),
        )
        .route("/api/questions/stats", get(handlers::get_question_stats))
        .route("/qa/rss.xml", get(handlers::get_question_rss))
        .route("/api/webhook", post(handlers::webhook))
        .layer(CorsLayer::permissive())
        .with_state(state);

    let listener = tokio::net::TcpListener::bind("127.0.0.1:3003").await?;
    println!("Listening on 127.0.0.1:3003");
    axum::serve(listener, app).await?;
    Ok(())
}
