use std::path::PathBuf;

use protobuf::Message as _;
use psql_scip::{config::Config, pipeline};
use scip::types::Index;
use tempfile::TempDir;

fn fixtures_dir() -> PathBuf {
    PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("tests")
        .join("fixtures")
}

fn make_config(tmp: &TempDir) -> Config {
    let out = tmp.path().join("index.scip");
    Config {
        schema_path: fixtures_dir().join("pipeline_schema.sql"),
        root_dir: fixtures_dir(),
        output_path: out,
        no_gitignore: false,
    }
}

fn make_self_contained_config(root: &TempDir) -> (Config, PathBuf) {
    let root_path = root.path().to_owned();

    let schema_sql = "CREATE TABLE users (id bigint NOT NULL, name text NOT NULL);";
    let query_sql = "SELECT id, name FROM users;";

    let schema_path = root_path.join("schema.sql");
    let query_path = root_path.join("query.sql");
    let output_path = root_path.join("index.scip");

    std::fs::write(&schema_path, schema_sql).expect("must write schema fixture");
    std::fs::write(&query_path, query_sql).expect("must write query fixture");

    let config = Config {
        schema_path,
        root_dir: root_path,
        output_path: output_path.clone(),
        no_gitignore: false,
    };
    (config, output_path)
}

fn decode_index(output_path: &PathBuf) -> Index {
    let bytes = std::fs::read(output_path).expect("index.scip must be readable");
    Index::parse_from_bytes(&bytes).expect("index.scip must be valid protobuf")
}

#[test]
fn pipeline_run_produces_index_file() {
    let tmp = TempDir::new().expect("tempdir must be creatable");
    let config = make_config(&tmp);
    let out_path = config.output_path.clone();

    let stats = pipeline::run(&config).expect("pipeline must succeed on valid fixtures");

    assert!(out_path.exists(), "index.scip must exist at output path");
    assert!(
        stats.files_processed >= 1,
        "at least one SQL file must be processed; got {}",
        stats.files_processed
    );
}

#[test]
fn pipeline_run_produces_nonzero_occurrences() {
    let tmp = TempDir::new().expect("tempdir must be creatable");
    let config = make_config(&tmp);

    let stats = pipeline::run(&config).expect("pipeline must succeed on valid fixtures");

    assert!(
        stats.total_occurrences > 0,
        "query referencing schema table must produce at least one occurrence; got {}",
        stats.total_occurrences
    );
}

#[test]
fn pipeline_output_file_is_nonempty_bytes() {
    let tmp = TempDir::new().expect("tempdir must be creatable");
    let config = make_config(&tmp);
    let out_path = config.output_path.clone();

    pipeline::run(&config).expect("pipeline must succeed on valid fixtures");

    let bytes =
        std::fs::read(&out_path).expect("output file must be readable after successful run");
    assert!(!bytes.is_empty(), "index.scip must contain protobuf bytes");
}

#[test]
fn document_relative_paths_are_not_absolute() {
    let tmp = TempDir::new().expect("tempdir must be creatable");
    let (config, output_path) = make_self_contained_config(&tmp);

    pipeline::run(&config).expect("pipeline must succeed with self-contained fixtures");

    let index = decode_index(&output_path);
    assert!(
        !index.documents.is_empty(),
        "index must contain at least one document"
    );
    for doc in &index.documents {
        assert!(
            !doc.relative_path.starts_with('/'),
            "relative_path must not be absolute; got: {:?}",
            doc.relative_path,
        );
    }
}
