use std::{error::Error, process::exit};

use colored::Colorize;
use reqwest::{header::USER_AGENT, Client};
use serde_json::Value;

use crate::{Config, UA};

pub async fn dl(cfg: Config, client: Client) -> Result<(usize, usize), Box<dyn Error>> {
    let mut dl = 0;
    let mut fl = 0;

    let (json_url, board) = parse_url(cfg.target.clone());
    println!("{}", format!("Board JSON URL: {json_url}").blue().bold());

    let (tc, threads) = parse_json(&client, json_url, board.clone()).await?;
    println!(
        "{}",
        format!("Current thread count of {board}: {tc}")
            .blue()
            .bold()
    );

    for url in threads {
        let (ndl, nfl) =
            crate::thread::dl(cfg.clone(), client.clone(), Some((url, board.clone()))).await?;

        dl += ndl;
        fl += nfl;
    }

    Ok((dl, fl))
}

fn parse_url(url: String) -> (String, String) {
    let url_split: Vec<&str> = url.split('/').collect();
    let board = url_split.get(url_split.len() - 2).unwrap();

    (
        format!("https://a.4cdn.org/{board}/catalog.json",),
        board.to_string(),
    )
}

async fn parse_json(
    client: &Client,
    url: String,
    board: String,
) -> Result<(usize, Vec<String>), Box<dyn Error>> {
    let mut threads = Vec::new();

    let res_txt = match client.get(url.clone()).header(USER_AGENT, UA).send().await {
        Ok(res) => res.text().await?,
        Err(e) => {
            eprintln!(
                "{}",
                format!("Failed to request the thread JSON data from {url}: {e}")
                    .red()
                    .bold()
            );
            exit(1);
        }
    };

    let data: Value = match serde_json::from_str(&res_txt) {
        Ok(data) => data,
        Err(e) => {
            eprintln!(
                "{}",
                format!("Failed to parse the raw data from {url}: {e}")
                    .red()
                    .bold()
            );
            exit(1);
        }
    };

    let catalogue: Vec<Value> = data
        .as_array()
        .unwrap()
        .iter()
        .map(|page| page["threads"].clone())
        .collect();

    for thread_array in catalogue {
        for thread in thread_array.as_array().unwrap() {
            let thread_id = &thread["no"];
            let url = format!("https://a.4cdn.org/{board}/thread/{thread_id}.json");

            threads.push(url);
        }
    }

    Ok((threads.len(), threads))
}
