use overshift::Manifest;

fn fixture_path() -> std::path::PathBuf {
	std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/sample_project")
}

#[test]
fn generate_snapshot() {
	let manifest = Manifest::load(fixture_path()).unwrap();
	let snapshot = overshift::snapshot::generate(&manifest).unwrap();

	assert!(snapshot.contains("Auto-generated by overshift"));
	assert!(snapshot.contains("Module: _shared"));
	assert!(snapshot.contains("Module: entity"));
	assert!(snapshot.contains("ANALYZER"));
	assert!(snapshot.contains("TABLE"));
	assert!(snapshot.contains("Summary:"));
}

#[test]
fn write_and_check_snapshot() {
	let tmp = tempfile::tempdir().unwrap();
	copy_dir(&fixture_path(), tmp.path());

	let manifest = Manifest::load(tmp.path()).unwrap();

	// Initially no snapshot exists
	assert!(!overshift::snapshot::check(&manifest).unwrap());

	// Write snapshot
	overshift::snapshot::write(&manifest).unwrap();

	// Now check passes
	assert!(overshift::snapshot::check(&manifest).unwrap());

	// Verify file exists
	let snapshot_path = tmp.path().join("generated/current.surql");
	assert!(snapshot_path.exists());
}

#[test]
fn snapshot_check_detects_stale() {
	let tmp = tempfile::tempdir().unwrap();
	copy_dir(&fixture_path(), tmp.path());

	let manifest = Manifest::load(tmp.path()).unwrap();

	// Write snapshot
	overshift::snapshot::write(&manifest).unwrap();
	assert!(overshift::snapshot::check(&manifest).unwrap());

	// Modify a schema file
	let table_path = tmp.path().join("schema/entity/table.surql");
	let mut content = std::fs::read_to_string(&table_path).unwrap();
	content.push_str("\nDEFINE TABLE OVERWRITE comment SCHEMAFULL;\n");
	std::fs::write(&table_path, content).unwrap();

	// Check should now fail
	assert!(!overshift::snapshot::check(&manifest).unwrap());

	// Re-write should fix it
	overshift::snapshot::write(&manifest).unwrap();
	assert!(overshift::snapshot::check(&manifest).unwrap());
}

#[test]
fn snapshot_creates_generated_dir() {
	let tmp = tempfile::tempdir().unwrap();
	copy_dir(&fixture_path(), tmp.path());

	// Remove generated dir if it exists
	let gen_dir = tmp.path().join("generated");
	let _ = std::fs::remove_dir_all(&gen_dir);
	assert!(!gen_dir.exists());

	let manifest = Manifest::load(tmp.path()).unwrap();
	overshift::snapshot::write(&manifest).unwrap();

	assert!(gen_dir.exists());
	assert!(gen_dir.join("current.surql").exists());
}

#[test]
fn snapshot_no_modules_generates_empty_notice() {
	let tmp = tempfile::tempdir().unwrap();
	std::fs::write(
		tmp.path().join("manifest.toml"),
		r#"
		[meta]
		ns = "test"
		db = "main"
		system_db = "_system"
	"#,
	)
	.unwrap();

	let manifest = Manifest::load(tmp.path()).unwrap();
	let snapshot = overshift::snapshot::generate(&manifest).unwrap();
	assert!(snapshot.contains("No schema modules found"));
}

#[test]
fn snapshot_is_deterministic() {
	let manifest = Manifest::load(fixture_path()).unwrap();
	let s1 = overshift::snapshot::generate(&manifest).unwrap();
	let s2 = overshift::snapshot::generate(&manifest).unwrap();
	assert_eq!(s1, s2);
}

#[test]
fn snapshot_includes_all_modules() {
	let manifest = Manifest::load(fixture_path()).unwrap();
	let snapshot = overshift::snapshot::generate(&manifest).unwrap();

	// Both modules present
	assert!(snapshot.contains("Module: _shared"));
	assert!(snapshot.contains("Module: entity"));

	// Content from both
	assert!(snapshot.contains("ANALYZER"));
	assert!(snapshot.contains("fn::greet"));
	assert!(snapshot.contains("fn::user_count"));
}

fn copy_dir(src: &std::path::Path, dst: &std::path::Path) {
	for entry in walkdir::WalkDir::new(src) {
		let entry = entry.unwrap();
		let rel = entry.path().strip_prefix(src).unwrap();
		let target = dst.join(rel);
		if entry.file_type().is_dir() {
			std::fs::create_dir_all(&target).ok();
		} else {
			if let Some(parent) = target.parent() {
				std::fs::create_dir_all(parent).ok();
			}
			std::fs::copy(entry.path(), &target).unwrap();
		}
	}
}
