// Copyright (C) 2026  The Software Heritage developers
// See the AUTHORS file at the top-level directory of this distribution
// License: GNU General Public License version 3, or any later version
// See top-level LICENSE file for more information

use std::sync::Arc;

use swh_contents::blame::blame::Blamer;
use swh_contents::diff::differ::RevDiffer;
use swh_graph::graph::SwhGraphWithProperties;
use swh_graph::labels::LabelNameId;
use swh_graph::{NodeType, SWHID};

use swh_osv::szz::strategies::{NaiveStrategyFactory, StrategyFactory};
use swh_osv::szz::tokenizers::StrippedAsciiWhitespaceTokenizer;
use swh_osv::szz::{IntroductionFile, IntroductionFiles};

type NoWriter = &'static mut Vec<u8>;

#[path = "./graphs.rs"]
mod graphs;
use graphs::*;

// inspired by https://stackoverflow.com/a/27582993/539465
macro_rules! map {
    ($($k:expr => $v:expr),* $(,)?) => {{
        vec![$(($k, $v),)*].into_iter().collect()
    }};
}
macro_rules! set {
    ($($v:expr),* $(,)?) => {{
        vec![$($v,)*].into_iter().collect()
    }};
}

#[tokio::test(flavor = "multi_thread")]
#[test_log::test]
async fn szz_strip_whitespace() {
    // rev1: original content
    // rev2: whitespace changes to both lines + adds bug line
    // rev3: fix (reverts to original content)
    let file1 = b"line1\nline2\n";
    let file2 = b"  line1  \nbad line2\nline3\n";
    let file3 = b"  line1  \n\tbad   line2 \n  line3 again  \n";
    let file4 = b"line1\n  good line2\n  line3 again  \n";

    let graph = Arc::new(get_n_rev_linear_graph(4));
    let content_retriever = get_content_retriever!(&graph, [file1, file2, file3, file4]);
    let differ = RevDiffer::new(graph.clone(), content_retriever.clone())
        .with_tokenizer(StrippedAsciiWhitespaceTokenizer);

    let stats = swh_osv::szz::SzzStats::default();
    let factory = NaiveStrategyFactory::new(graph.clone());
    let blamer = Blamer::new(graph.clone(), content_retriever.clone())
        .with_tokenizer(StrippedAsciiWhitespaceTokenizer);
    let processor = swh_osv::szz::SzzProcessor::new(
        graph.clone(),
        content_retriever.clone(),
        NaiveStrategyFactory::new(graph.clone()),
        blamer,
    );
    let mut strategy = factory.get_instance("TEST-0001.json", &stats).unwrap();

    let result = processor
        .szz(&differ, &mut strategy, 4, &mut None::<NoWriter>)
        .await
        .expect("SZZ failed");

    let expected = map! {
        2 => IntroductionFiles(map!{
            Box::from([LabelNameId(0)]) => IntroductionFile { line_ranges: set!{ 1..2 } },
        }),
    };
    assert_eq!(result, expected);
}
