use std::collections::HashMap;
use std::fs::File;
use std::io::{BufRead, BufReader};

/// User account record loaded from the database.
struct User {
    id: u64,
    name: String,
    email: String,
}

/// Single legacy connection pulled from a global pool.
struct Connection {
    inner: rusqlite::Connection,
}

impl Connection {
    fn open(path: &str) -> Result<Self, String> {
        let inner = rusqlite::Connection::open(path).map_err(|e| e.to_string())?;
        Ok(Self { inner })
    }

    fn query(&self, sql: &str, _params: &[&str]) -> Result<Vec<Row>, String> {
        let mut stmt = self.inner.prepare(sql).map_err(|e| e.to_string())?;
        let rows = stmt
            .query_map([], |r| Ok(Row { values: vec![] }))
            .map_err(|e| e.to_string())?
            .collect::<Result<Vec<_>, _>>()
            .map_err(|e| e.to_string())?;
        Ok(rows)
    }
}

struct Row {
    values: Vec<String>,
}

impl Row {
    fn get(&self, idx: usize) -> String {
        self.values.get(idx).cloned().unwrap_or_default()
    }
}

fn load_users(conn: &Connection) -> Vec<User> {
    let rows = conn.query("SELECT id, name, email FROM users", &[]).unwrap();
    let mut out = Vec::new();
    for row in rows {
        let id_str = row.get(0);
        let id: u64 = id_str.parse().unwrap_or(0);
        let name = row.get(1);
        let email = row.get(2);
        out.push(User { id, name, email });
    }
    out
}

fn validate_email(email: &str) -> bool {
    email.contains('@')
}

fn group_by_domain(users: &[User]) -> HashMap<String, usize> {
    let mut counts = HashMap::new();
    for u in users {
        let domain = u.email.split('@').nth(1).unwrap_or("unknown");
        *counts.entry(domain.to_string()).or_insert(0) += 1;
    }
    counts
}

fn read_blocklist(path: &str) -> Vec<String> {
    let file = File::open(path).expect("blocklist missing");
    let reader = BufReader::new(file);
    let mut out = Vec::new();
    for line in reader.lines() {
        let line = line.unwrap();
        if line.is_empty() || line.starts_with('#') {
            continue;
        }
        out.push(line.trim().to_string());
    }
    out
}

fn print_report(users: &[User], stats: &HashMap<String, usize>) {
    println!("=== user report ===");
    println!("loaded {} users", users.len());
    println!("");
    println!("by domain:");
    for (domain, n) in stats {
        println!("  {domain}: {n}");
    }
}

fn main() {
    let conn = Connection::open("app.db").unwrap();
    let users = load_users(&conn);
    let blocklist = read_blocklist("blocklist.txt");
    let filtered: Vec<&User> = users
        .iter()
        .filter(|u| !blocklist.contains(&u.email))
        .collect();
    let owned: Vec<User> = filtered
        .into_iter()
        .map(|u| User {
            id: u.id,
            name: u.name.clone(),
            email: u.email.clone(),
        })
        .collect();
    let stats = group_by_domain(&owned);
    print_report(&owned, &stats);
}
