//! Bizzy HTML VAT scrape — mirrors `enrich_nl_companies_vat_from_bizzy` from
//! `scripts/scrape_global_registry.py`.

use std::sync::Arc;
use std::time::Duration;

use once_cell::sync::Lazy;
use regex::Regex;
use serde_json::{Map, Value};
use tokio::sync::Semaphore;

use super::northdata::DEFAULT_UA;

const BIZZY_PREFIX: &str = "https://bizzy.ai/nl/nl/";
const HTML_SNIPPET_LEN: usize = 8000;
const MAX_CONCURRENT: usize = 3;
const REQUEST_PAUSE_SEC: f64 = 0.4;
const HTTP_TIMEOUT_SEC: u64 = 20;

static RE_VAT: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?i)NL\s*(\d{9}B\d{2})").unwrap());

fn kvk_digits(v: Option<&Value>) -> Option<String> {
    let v = v?;
    let s = if let Some(i) = v.as_i64() {
        i.to_string()
    } else if let Some(s) = v.as_str() {
        s.trim().to_string()
    } else {
        v.to_string().trim_matches('"').to_string()
    };
    if s.chars().all(|c| c.is_ascii_digit()) && !s.is_empty() {
        Some(s)
    } else {
        None
    }
}

fn parse_vat_from_bizzy_html(html: &str) -> Option<String> {
    let chunk = if let Some(idx) = html.find("og:title") {
        html.get(idx..idx + HTML_SNIPPET_LEN.min(html.len().saturating_sub(idx)))
            .unwrap_or(html)
    } else {
        html.get(..HTML_SNIPPET_LEN.min(html.len())).unwrap_or(html)
    };
    let caps = RE_VAT.captures(chunk)?;
    Some(format!("NL{}", caps.get(1)?.as_str().to_uppercase()))
}

async fn fetch_vat_for_kvk(client: &reqwest::Client, kvk_s: &str) -> Option<String> {
    let url = format!("{BIZZY_PREFIX}{kvk_s}");
    let resp = client
        .get(url)
        .header(
            "accept",
            "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
        )
        .header("accept-language", "en-US,en;q=0.9,nl;q=0.8")
        .header("user-agent", DEFAULT_UA)
        .timeout(Duration::from_secs(HTTP_TIMEOUT_SEC))
        .send()
        .await
        .ok()?;
    if !resp.status().is_success() {
        return None;
    }
    let html = resp.text().await.ok()?;
    parse_vat_from_bizzy_html(&html)
}

/// Set `vat_id` on each company when KVK omitted `btwNummer` (in-place).
pub async fn enrich_nl_companies_vat_from_bizzy(
    client: &reqwest::Client,
    companies: &mut [Map<String, Value>],
) {
    let sem = Arc::new(Semaphore::const_new(MAX_CONCURRENT));
    let mut jobs: Vec<(usize, String)> = Vec::new();
    for (i, c) in companies.iter().enumerate() {
        if c.get("vat_id")
            .and_then(|v| v.as_str())
            .map(|s| !s.is_empty())
            == Some(true)
        {
            continue;
        }
        let Some(kvk_s) = kvk_digits(c.get("kvk_number")) else {
            continue;
        };
        jobs.push((i, kvk_s));
    }

    let mut handles = Vec::new();
    for (i, kvk_s) in jobs {
        let client = client.clone();
        let sem = Arc::clone(&sem);
        handles.push(tokio::spawn(async move {
            let _p = match sem.acquire().await {
                Ok(p) => p,
                Err(_) => return (i, None),
            };
            let vat = fetch_vat_for_kvk(&client, &kvk_s).await;
            tokio::time::sleep(Duration::from_secs_f64(REQUEST_PAUSE_SEC)).await;
            (i, vat)
        }));
    }

    for h in handles {
        if let Ok((i, vat_opt)) = h.await {
            if let Some(vat) = vat_opt {
                if let Some(c) = companies.get_mut(i) {
                    c.insert("vat_id".to_string(), Value::String(vat));
                }
            }
        }
    }
}
