// 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 anyhow::{Context, Result};
use clap::Parser;
use env_logger::Env;
use log::info;
use rayon::prelude::*;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::PathBuf;
use swh_graph::graph::*;
use swh_graph::mph::DynMphf;
use swh_graph_stdlib::origins;

/// Approximate search of origins in the Software Heritage graph, abstracting over common repository
/// URL variations (e.g., caseness, URI protocols, `.git` suffix.)
#[derive(Parser, Debug)]
#[command(about, long_about = None)]
pub struct CliArgs {
    /// Basename of the input Software Heritage graph to load.
    #[arg(short, long, value_name = "BASENAME")]
    pub graph: PathBuf,

    /// Filename of a textual file containing the origins to search for, one per line.
    #[arg(short, long, value_name = "FILENAME")]
    pub origins: PathBuf,
}

/// Read origin URLs from a file, one per line, returning them as a vector of byte vectors.
fn read_origins(fname: &PathBuf) -> Result<Vec<String>> {
    let file = File::open(fname)
        .with_context(|| format!("Cannot open origins file: {}", fname.display()))?;
    let origins: Vec<String> = BufReader::new(file)
        .lines()
        .map(|line| line.context("Cannot read origin line from file"))
        .collect::<Result<Vec<String>>>()?;

    Ok(origins)
}

/// Main program.
pub fn main() -> Result<()> {
    let args = CliArgs::parse();
    env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();

    info!("Loading graph...");
    let graph = swh_graph::graph::SwhBidirectionalGraph::new(args.graph)
        .context("Could not load graph")?
        .init_properties()
        .load_properties(|properties| properties.load_maps::<DynMphf>())
        .expect("Could not load maps")
        .load_properties(|properties| properties.load_strings())
        .expect("Could not load strings");
    let props = graph.properties();
    let origins = read_origins(&args.origins)?;

    info!("Processing origin nodes...");
    origins::fuzzy_find_origins(&graph, &origins).for_each(|(input_pos, origin_node)| {
        println!(
            "{}\t{}\t{}",
            &origins[input_pos],
            origin_node,
            // Note: the two unwraps before should not fail. The one extracting URLs (messages)
            // because to be a match, an origin should have an URL. The one for UTF8 conversion
            // because URLs should all be UTF8-safe.
            String::from_utf8(props.message(origin_node).unwrap()).unwrap()
        )
    });
    info!("All done.");
    Ok(())
}
