//
// Syd: rock-solid application kernel
// bench/canon.rs: Benchmarks for syd::fs::safe_canonicalize()
//
// Copyright (c) 2023, 2024, 2025 Ali Polatel <alip@chesswob.org>
//
// SPDX-License-Identifier: GPL-3.0

use std::{
    fs::{create_dir_all, remove_dir_all, File},
    os::unix::fs::symlink,
};

use brunch::{benches, Bench};
use nix::unistd::Pid;
use syd::{
    lookup::{safe_canonicalize, FsFlags},
    path::XPathBuf,
};
use tempfile::tempdir;

fn setup_paths() -> (XPathBuf, XPathBuf, XPathBuf, XPathBuf, XPathBuf) {
    let temp_dir = tempdir().expect("Failed to create a temp dir");
    let temp_dir_path = temp_dir.path();

    // Existing path with symlinks
    let existing_path = temp_dir_path.join("existing");
    create_dir_all(&existing_path).expect("Failed to create existing path");
    let symlink_path = temp_dir_path.join("symlink");
    symlink(&existing_path, &symlink_path).expect("Failed to create symlink");

    // Self-referencing loop
    let loop_path = temp_dir_path.join("loop");
    create_dir_all(&loop_path).expect("Failed to create loop path");
    let loop_symlink = loop_path.join("self_loop");
    symlink(&loop_path, &loop_symlink).expect("Failed to create self-referencing symlink");

    // Non-existing path
    let non_existing_path = temp_dir_path.join("non_existing");

    // Complex structure setup
    let complex_base = temp_dir_path.join("syd-test");
    let complex_a = complex_base.join("a");
    let complex_1 = complex_base.join("1");
    let complex_target = complex_a.join("target.txt");
    let complex_link_to_a = complex_1.join("2/3/link_to_a");
    let complex_link_to_1 = complex_a.join("b/c/d/link_to_1");
    let complex_link_to_c = complex_1.join("2/3/link_to_c");

    create_dir_all(complex_a.join("b/c/d/e/f"))
        .expect("Failed to create complex a directory structure");
    create_dir_all(complex_1.join("2/3")).expect("Failed to create complex 1 directory structure");
    File::create(&complex_target).expect("Failed to create target file");

    symlink("../../../a", &complex_link_to_a).expect("Failed to create symlink to a");
    symlink("../../../../1", &complex_link_to_1).expect("Failed to create symlink to 1");
    symlink("../../../a/b/c", &complex_link_to_c).expect("Failed to create symlink to c");

    let complex_path = complex_link_to_a.join("b/c/d/e/f/../../../../../b/c/d/link_to_1/../../syd-test/1/2/3/link_to_c/d/e/f/../../link_to_1/2/../././../a/.././a/target.txt");

    (
        temp_dir_path.to_path_buf().into(),
        non_existing_path.into(),
        symlink_path.into(),
        loop_symlink.into(),
        complex_path.into(),
    )
}

fn main() {
    let pid = Pid::this();
    let (temp_dir_path, non_existing, symlink, loop_path, complex_path) = setup_paths();

    // Init preopen FDs that canonicalize expects.
    syd::config::proc_init().unwrap();

    benches!(
        inline:

        // --- Complex path benches ---
        Bench::new("safe_canonicalize_complex_empty")
            .run(|| {
                safe_canonicalize(
                    pid,
                    Some(libc::AT_FDCWD),
                    &complex_path,
                    FsFlags::empty(),
                    None,
                ).ok()
            }),
        Bench::new("safe_canonicalize_complex_MUST_PATH")
            .run(|| {
                safe_canonicalize(
                    pid,
                    Some(libc::AT_FDCWD),
                    &complex_path,
                    FsFlags::MUST_PATH,
                    None,
                ).ok()
            }),
        Bench::new("safe_canonicalize_complex_MISS_LAST")
            .run(|| {
                safe_canonicalize(
                    pid,
                    Some(libc::AT_FDCWD),
                    &complex_path,
                    FsFlags::MISS_LAST,
                    None,
                ).ok()
            }),

        // --- Non‑existing path benches ---
        Bench::new("safe_canonicalize_non_existing_empty")
            .run(|| {
                safe_canonicalize(
                    pid,
                    Some(libc::AT_FDCWD),
                    &non_existing,
                    FsFlags::empty(),
                    None,
                ).ok()
            }),
        Bench::new("safe_canonicalize_non_existing_MUST_PATH")
            .run(|| {
                safe_canonicalize(
                    pid,
                    Some(libc::AT_FDCWD),
                    &non_existing,
                    FsFlags::MUST_PATH,
                    None,
                ).ok()
            }),
        Bench::new("safe_canonicalize_non_existing_MISS_LAST")
            .run(|| {
                safe_canonicalize(
                    pid,
                    Some(libc::AT_FDCWD),
                    &non_existing,
                    FsFlags::MISS_LAST,
                    None,
                ).ok()
            }),

        // --- Symlink path benches ---
        Bench::new("safe_canonicalize_symlink_empty")
            .run(|| {
                safe_canonicalize(
                    pid,
                    Some(libc::AT_FDCWD),
                    &symlink,
                    FsFlags::empty(),
                    None,
                ).ok()
            }),
        Bench::new("safe_canonicalize_symlink_MUST_PATH")
            .run(|| {
                safe_canonicalize(
                    pid,
                    Some(libc::AT_FDCWD),
                    &symlink,
                    FsFlags::MUST_PATH,
                    None,
                ).ok()
            }),
        Bench::new("safe_canonicalize_symlink_MISS_LAST")
            .run(|| {
                safe_canonicalize(
                    pid,
                    Some(libc::AT_FDCWD),
                    &symlink,
                    FsFlags::MISS_LAST,
                    None,
                ).ok()
            }),

        // --- Loop path benches ---
        Bench::new("safe_canonicalize_loop_empty")
            .run(|| {
                safe_canonicalize(
                    pid,
                    Some(libc::AT_FDCWD),
                    &loop_path,
                    FsFlags::empty(),
                    None,
                ).ok()
            }),
        Bench::new("safe_canonicalize_loop_MUST_PATH")
            .run(|| {
                safe_canonicalize(
                    pid,
                    Some(libc::AT_FDCWD),
                    &loop_path,
                    FsFlags::MUST_PATH,
                    None,
                ).ok()
            }),
        Bench::new("safe_canonicalize_loop_MISS_LAST")
            .run(|| {
                safe_canonicalize(
                    pid,
                    Some(libc::AT_FDCWD),
                    &loop_path,
                    FsFlags::MISS_LAST,
                    None,
                ).ok()
            }),
    );

    let _ = remove_dir_all(temp_dir_path);
}
