// Copyright (C) 2024-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

//! Standard library to work on Software Heritage compressed graph in Rust

use anyhow::{Result, ensure};

use swh_graph::NodeType;
use swh_graph::graph::*;
use swh_graph::labels::{EdgeLabel, VisitStatus};
use swh_graph::properties;

/// Given a graph and an origin node in it, return the node id and timestamp
/// (as a number of seconds since Epoch) of the most recent snapshot of that
/// origin, if it exists.
///
/// Note: only visit with status `Full` are considered when selecting
/// snapshots.
pub fn find_latest_snp<G>(graph: &G, ori: NodeId) -> Result<Option<(NodeId, u64)>>
where
    G: SwhLabeledForwardGraph + SwhGraphWithProperties,
    <G as SwhGraphWithProperties>::Maps: properties::Maps,
{
    let props = graph.properties();
    let node_type = props.node_type(ori);
    ensure!(
        node_type == NodeType::Origin,
        "Type of {ori} should be ori, but is {node_type} instead"
    );
    // Most recent snapshot thus far, as an optional (node_id, timestamp) pair
    let mut latest_snp: Option<(usize, u64)> = None;
    for (succ, labels) in graph.labeled_successors(ori) {
        let node_type = props.node_type(succ);
        if node_type != NodeType::Snapshot {
            continue;
        }
        for label in labels {
            if let EdgeLabel::Visit(visit) = label {
                if visit.status() != VisitStatus::Full {
                    continue;
                }
                let ts = visit.timestamp();
                if let Some((_cur_snp, cur_ts)) = latest_snp {
                    if ts > cur_ts {
                        latest_snp = Some((succ, ts))
                    }
                } else {
                    latest_snp = Some((succ, ts))
                }
            }
        }
    }
    Ok(latest_snp)
}

pub mod collections;
pub mod connectivity;
pub mod labeling;

pub mod fs;

pub mod origins;

#[cfg(feature = "unstable_project_ids")]
pub mod project_ids;

mod root_directory;
pub use root_directory::find_root_dir;

pub mod vcs;

mod visit;
pub use visit::*;

pub mod diff;

pub use deprecated::*;

#[allow(deprecated)]
mod deprecated {
    use std::collections::HashMap;
    use swh_graph::labels::{LabelNameId, Permission};

    use super::*;

    #[doc(hidden)]
    #[deprecated(since = "10.3.0", note = "use swh_graph_stdlib::vcs::HEAD_REF_NAMES")]
    pub const HEAD_REF_NAMES: [&str; 2] = vcs::HEAD_REF_NAMES;

    #[doc(hidden)]
    #[deprecated(since = "10.3.0", note = "use swh_graph_stdlib::vcs::find_head_rev")]
    pub fn find_head_rev<G>(graph: &G, snp: NodeId) -> Result<Option<NodeId>>
    where
        G: SwhLabeledForwardGraph + SwhGraphWithProperties,
        <G as SwhGraphWithProperties>::LabelNames: properties::LabelNames,
        <G as SwhGraphWithProperties>::Maps: properties::Maps,
    {
        vcs::find_head_rev(graph, snp)
    }

    #[doc(hidden)]
    #[deprecated(
        since = "10.3.0",
        note = "use swh_graph_stdlib::vcs::find_head_rev_by_refs"
    )]
    pub fn find_head_rev_by_refs<G>(
        graph: &G,
        snp: NodeId,
        ref_name_ids: &[LabelNameId],
    ) -> Result<Option<NodeId>>
    where
        G: SwhLabeledForwardGraph + SwhGraphWithProperties,
        <G as SwhGraphWithProperties>::Maps: properties::Maps,
        <G as SwhGraphWithProperties>::LabelNames: properties::LabelNames,
    {
        vcs::find_head_rev_by_refs(graph, snp, ref_name_ids)
    }

    #[doc(hidden)]
    #[deprecated(since = "10.3.0", note = "use swh_graph_stdlib::fs::FsTree")]
    #[derive(Debug, Default, PartialEq)]
    pub enum FsTree {
        #[default]
        Content,
        Directory(HashMap<Vec<u8>, (FsTree, Option<Permission>)>),
        Revision(NodeId),
    }

    impl From<fs::FsTree> for FsTree {
        fn from(tree: fs::FsTree) -> Self {
            match tree {
                fs::FsTree::Content => FsTree::Content,
                fs::FsTree::Directory(entries) => FsTree::Directory(
                    entries
                        .into_iter()
                        .map(|(name, (tree, perm))| (name, (tree.into(), perm)))
                        .collect(),
                ),
                fs::FsTree::Revision(rev) => FsTree::Revision(rev),
            }
        }
    }

    #[doc(hidden)]
    #[deprecated(since = "10.3.0", note = "use swh_graph_stdlib::fs::resolve_name")]
    pub fn fs_resolve_name<G>(
        graph: &G,
        dir: NodeId,
        name: impl AsRef<[u8]>,
    ) -> Result<Option<NodeId>>
    where
        G: SwhLabeledForwardGraph + SwhGraphWithProperties,
        <G as SwhGraphWithProperties>::LabelNames: properties::LabelNames,
        <G as SwhGraphWithProperties>::Maps: properties::Maps,
    {
        fs::resolve_name(graph, dir, name)
    }

    pub fn fs_resolve_name_by_id<G>(
        graph: &G,
        dir: NodeId,
        name: LabelNameId,
    ) -> Result<Option<NodeId>>
    where
        G: SwhLabeledForwardGraph + SwhGraphWithProperties,
        <G as SwhGraphWithProperties>::Maps: properties::Maps,
    {
        fs::resolve_name_by_id(graph, dir, name)
    }

    #[doc(hidden)]
    #[deprecated(since = "10.3.0", note = "use swh_graph_stdlib::fs::resolve_path")]
    pub fn fs_resolve_path<G>(
        graph: &G,
        dir: NodeId,
        path: impl AsRef<[u8]>,
    ) -> Result<Option<NodeId>>
    where
        G: SwhLabeledForwardGraph + SwhGraphWithProperties,
        <G as SwhGraphWithProperties>::LabelNames: properties::LabelNames,
        <G as SwhGraphWithProperties>::Maps: properties::Maps,
    {
        fs::resolve_path(graph, dir, path)
    }

    #[doc(hidden)]
    #[deprecated(
        since = "10.3.0",
        note = "use swh_graph_stdlib::fs::resolve_path_by_id"
    )]
    pub fn fs_resolve_path_by_id<G>(
        graph: &G,
        dir: NodeId,
        path: &[LabelNameId],
    ) -> Result<Option<NodeId>>
    where
        G: SwhLabeledForwardGraph + SwhGraphWithProperties,
        <G as SwhGraphWithProperties>::Maps: properties::Maps,
    {
        fs::resolve_path_by_id(graph, dir, path)
    }

    #[doc(hidden)]
    #[deprecated(since = "10.3.0", note = "use swh_graph_stdlib::fs::ls_tree")]
    pub fn fs_ls_tree<G>(graph: &G, dir: NodeId) -> Result<FsTree>
    where
        G: SwhLabeledForwardGraph + SwhGraphWithProperties,
        <G as SwhGraphWithProperties>::LabelNames: properties::LabelNames,
        <G as SwhGraphWithProperties>::Maps: properties::Maps,
    {
        fs::ls_tree(graph, dir).map(Into::into)
    }
}
