// Copyright (C) 2025  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::path::PathBuf;

use anyhow::{Context, Result};
use clap::Parser;
use epserde::deser::Deserialize;
use epserde::prelude::Flags;
use mimalloc::MiMalloc;
use swh_graph::graph::SwhUnidirectionalGraph;
use swh_graph::mph::DynMphf;
use swh_graph_stdlib::connectivity::SubgraphWccs;

use swh_osv::bvgraph_to_parquet::BvgraphToParquet;
use swh_osv::multimap::BvNodeMultimap;

#[global_allocator]
static GLOBAL: MiMalloc = MiMalloc;

#[derive(Parser, Debug)]
/// Writes an exhaustive list of all commits between an "introduced" commit and a
/// "fixed"/"limit"/"last_affected" commit.
struct Args {
    #[arg(long)]
    /// Path to swh-graph's files
    graph: PathBuf,
    #[arg(long)]
    /// Path to the epserde-serialized `SubgraphWccs` written by `compute-connected-components`
    subgraphwccs: Option<PathBuf>,
    #[arg(long)]
    /// Path of the map to read
    multimap: PathBuf,
    #[arg(long)]
    /// Path to the Parquet database to write
    parquet_out: PathBuf,
}

pub fn main() -> Result<()> {
    let args = Args::parse();

    tracing_subscriber::fmt()
        .with_env_filter(
            tracing_subscriber::EnvFilter::from_default_env()
                .add_directive(tracing::Level::INFO.into()),
        )
        .init();

    let graph = SwhUnidirectionalGraph::new(&args.graph)
        .with_context(|| format!("Could not load graph from {}", args.graph.display()))?
        .init_properties()
        .load_properties(|properties| properties.load_maps::<DynMphf>())
        .context("Could not load maps")?;
    let graph = &graph;

    let map = BvNodeMultimap::mmap(&args.multimap)
        .with_context(|| format!("Could not mmap from {}", args.multimap.display()))?;

    let converter = BvgraphToParquet::new(graph, args.parquet_out, "revision", "vulnerability")
        .with_key_swhids();

    match args.subgraphwccs {
        Some(path) => {
            tracing::info!("Loading SubgraphWccs...");
            let wccs = unsafe { <SubgraphWccs>::mmap(&path, Flags::RANDOM_ACCESS) }.with_context(
                || {
                    format!(
                        "Could not mmap connected components from {}",
                        path.display()
                    )
                },
            )?;
            let wccs = wccs.uncase();
            converter
                .with_contraction(
                    wccs.contraction(),
                    /* uncontract_keys: */ true,
                    /* uncontract_values: */ false,
                )
                .context("Incompatible subgraphwccs")?
                .convert(map.0)
        }
        None => converter.convert(map.0),
    }
}
