/*
    Request a colle from BJColle given its ID (and secret).
    URL : https://bjcolle.fr/students_oral_disp.php?colle=<colle_id>&hgfebrgl8ri3h=<secret>
*/

use crate::{api::{Colle, ColleAttachment}, parser::utils};
use reqwest;
use scraper::{ElementRef, Html, Selector};

pub async fn fetch(
    colle_id: &str,
    secret: &str,
) -> Result<Option<Colle>, Box<dyn std::error::Error>> {
    // Retrieve page content
    let url = format!(
        "https://bjcolle.fr/students_oral_disp.php?colle={}&hgfebrgl8ri3h={}",
        colle_id, secret
    );
    let response = reqwest::get(url).await?.text().await?;

    let document = Html::parse_document(&response);

    let header_selector = "#envelope > header > table > tbody h2";
    let header_selector = Selector::parse(header_selector).unwrap();

    // Expect ONLY ONE header
    let Some(header) = document.select(&header_selector).next() else {
        // Header not found error
        eprint!("Header not found in the document.");
        return Ok(None);
    };

    // Use the header text to extract informations :
    // - subject
    // - examiner
    // - date
    // - room
    // - student name
    let header_text = header.text().collect::<Vec<_>>();
    let (title, date, room, student) = if header_text.len() >= 4 {
        (
            header_text[0].trim().to_string(),
            header_text[1].trim().to_string(),
            header_text[2].trim().to_string(),
            header_text[3].trim().to_string(),
        )
    } else {
        // TODO: Handle the case where no header is found
        print!("Header text does not contain enough elements.");
        return Ok(None);
    };

    let (subject, examiner) = title
        .split("avec")
        .map(|s| s.trim())
        .collect::<Vec<_>>()
        .split_first()
        .map(|(subject, examiner)| {
            (
                subject
                    .to_string()
                    .replace("Colle de", "")
                    .replace("Colle d'", "")
                    .trim()
                    .to_string(),
                examiner.join(" "),
            )
        })
        .unwrap_or((String::new(), String::new()));

    // Parse the date and room
    let date = utils::parse_french_datetime(&date)?;
    let room = room.replace("Salle :", "").trim().to_string();

    // Parse grades and comments
    let grade_selector = "#haut > div > input#NOTE_ELEVE";
    let grade_selector = Selector::parse(grade_selector).unwrap();

    let grade = document
        .select(&grade_selector)
        .next()
        .and_then(|e| e.value().attr("value"))
        .unwrap_or("")
        .to_string()
        .replace(",", ".")
        .parse()
        .unwrap_or(-1.0);

    // Attachments
    let attachment_selector =  Selector::parse(".bj > a.bouton_eleve").unwrap();
    let files = document
        .select(&attachment_selector)
        .filter_map(|el| {
            let href = el.value().attr("href")?.to_string();
            let text = el.text().collect::<String>().trim().to_string();
            Some(ColleAttachment {
                url: href,
                name: text,
            })
        })
        .collect();

    // Return a JSON object with the parsed information
    Ok(Some(Colle {
        date,
        room,
        student,
        subject,
        examiner,
        bjid: Some(colle_id.to_string()),
        bjsecret: Some(secret.to_string()),
        grade: if grade >= 0.0 { Some(grade) } else { None },
        content: Some(extract_section(&document, "Sujet", true)),
        comment: Some(extract_section(&document, "Commentaire", false)),
        attachments: Some(files),
    }))
}

fn extract_section(document: &Html, keyword: &str, use_first_span: bool) -> String {
    let legend_selector = Selector::parse("fieldset.bj > legend.bj2").unwrap();
    let span_selector = Selector::parse("fieldset.bj > span").unwrap();

    // Check if legend contains the keyword ("Sujet" or "Commentaire")
    let is_match = document
        .select(&legend_selector)
        .any(|el| el.text().collect::<String>().contains(keyword));

    if !is_match {
        return String::new();
    }

    // Collect all matching spans
    let spans: Vec<_> = document.select(&span_selector).collect();

    let span_el = if spans.is_empty() {
        None
    } else if use_first_span {
        Some(spans[0].clone())
    } else {
        Some(spans[spans.len() - 1].clone())
    };

    let mut results = Vec::new();

    if let Some(span) = span_el {
        // Filter children != <br>
        let children: Vec<_> = span
            .children()
            .filter_map(ElementRef::wrap)
            .filter(|el| el.value().name() != "br")
            .collect();

        if !children.is_empty() {
            for child in children {
                results.push(child.html());
            }
        } else {
            results.push(span.html());
        }
    }

    utils::clean_content(&results.join("<br>"))
}
