//! Shared fixtures and helpers for sift-core integration tests.

#![allow(dead_code)]

use std::fs;
use std::path::{Path, PathBuf};

use sift_core::{
    CorpusKind, CorpusMeta, CorpusSpec, FilterMeta, IgnoreConfig, IndexConfig, IndexKind,
    IndexStore, IndexWalkOptions, Indexes, StoreMeta, TrigramIndex, VisibilityConfig, WalkMeta,
};

pub fn sample_store_meta(root: PathBuf, indexes: Vec<IndexKind>) -> StoreMeta {
    StoreMeta::new(
        CorpusMeta {
            root,
            kind: CorpusKind::Directory,
            include_paths: Vec::new(),
            exclude_paths: Vec::new(),
        },
        WalkMeta {
            follow_links: false,
            one_file_system: false,
            max_depth: None,
            max_filesize: None,
        },
        FilterMeta {
            visibility: VisibilityConfig::default(),
        },
        indexes,
    )
}

pub fn make_parity_corpus(root: &Path) {
    fs::create_dir_all(root.join("a")).expect("create dir");
    fs::create_dir_all(root.join("b")).expect("create dir");
    fs::write(root.join("a/x.txt"), "alpha beta\n").expect("write");
    fs::write(root.join("b/y.txt"), "gamma delta\n").expect("write");
}

pub fn make_filter_corpus(root: &Path) {
    fs::create_dir_all(root.join("a")).expect("create dir");
    fs::create_dir_all(root.join("a/.secret")).expect("create dir");
    fs::create_dir_all(root.join("subdir")).expect("create dir");
    fs::create_dir_all(root.join("skip")).expect("create dir");
    fs::create_dir_all(root.join("also_skip")).expect("create dir");

    fs::write(root.join("a/x.txt"), "alpha beta gamma\n").expect("write");
    fs::write(root.join("a/.hidden.txt"), "beta in hidden file\n").expect("write");
    fs::write(root.join("a/data.rs"), "fn main() {}\n").expect("write");
    fs::write(root.join("a/.secret/log"), "beta in hidden dir\n").expect("write");
    fs::write(root.join("subdir/a.txt"), "beta in subdir\n").expect("write");
    fs::write(root.join("subdir/b.log"), "no match here\n").expect("write");
    fs::write(root.join("root.txt"), "beta at root level\n").expect("write");
    fs::write(root.join("skip/ignored.txt"), "beta gitignored\n").expect("write");
    fs::write(root.join("also_skip/omit.txt"), "beta in .ignore\n").expect("write");
    fs::write(root.join("keep.txt"), "beta outside ignore rules\n").expect("write");

    fs::write(root.join(".gitignore"), "skip/**\n").expect("write gitignore");
    fs::write(root.join(".ignore"), "also_skip/**\n").expect("write ignore");
}

pub fn standard_build_config<'a>(root: &'a Path, exclude_paths: &'a [PathBuf]) -> IndexConfig<'a> {
    IndexConfig {
        corpus: CorpusSpec {
            root,
            kind: CorpusKind::Directory,
            follow_links: false,
            include_paths: &[],
            exclude_paths,
        },
        walk: IndexWalkOptions::new(false),
        visibility: VisibilityConfig::default(),
    }
}

pub fn no_ignore_build_config<'a>(root: &'a Path, exclude_paths: &'a [PathBuf]) -> IndexConfig<'a> {
    IndexConfig {
        corpus: CorpusSpec {
            root,
            kind: CorpusKind::Directory,
            follow_links: false,
            include_paths: &[],
            exclude_paths,
        },
        walk: IndexWalkOptions::new(false),
        visibility: VisibilityConfig {
            ignore: IgnoreConfig::disabled(),
            ..VisibilityConfig::default()
        },
    }
}

pub fn build_store(corpus: &Path, sift_dir: &Path) -> IndexStore {
    let root = corpus
        .canonicalize()
        .unwrap_or_else(|_| corpus.to_path_buf());
    let meta = sample_store_meta(root, vec![IndexKind::Trigram]);
    let mut store = IndexStore::open_or_create(sift_dir, &meta).expect("open store");
    let config = standard_build_config(corpus, &[]);
    store
        .build(&[IndexKind::Trigram], &config, &[])
        .expect("build index");
    store
}

pub fn open_indexes(sift_dir: &Path) -> Indexes {
    Indexes::open(sift_dir).expect("open indexes")
}

pub fn build_trigram_in_dir(corpus: &Path, trigram_dir: &Path) -> TrigramIndex {
    let (root, kind, include_paths) = if corpus.is_file() {
        let parent = corpus.parent().unwrap_or(corpus);
        let filename = corpus.file_name().map(PathBuf::from).unwrap_or_default();
        (parent, CorpusKind::SingleFile, vec![filename])
    } else {
        (corpus, CorpusKind::Directory, vec![])
    };
    let config = IndexConfig {
        corpus: CorpusSpec {
            root,
            kind,
            follow_links: false,
            include_paths: &include_paths,
            exclude_paths: &[],
        },
        walk: IndexWalkOptions::new(false),
        visibility: VisibilityConfig::default(),
    };
    TrigramIndex::build(&config, trigram_dir, &[]).expect("build trigram index")
}

pub fn dir_size(path: &Path) -> u64 {
    let mut total = 0u64;
    let Ok(entries) = fs::read_dir(path) else {
        return 0;
    };
    for entry in entries.flatten() {
        let path = entry.path();
        if path.is_dir() {
            total = total.saturating_add(dir_size(&path));
        } else if let Ok(meta) = fs::metadata(&path) {
            total = total.saturating_add(meta.len());
        }
    }
    total
}
