//! Schema snapshot generation — produces `generated/current.surql`.
//!
//! Combines all schema modules in dependency order into a single file that
//! represents the current declarative schema state.

use crate::Error;
use crate::manifest::Manifest;
use crate::schema::load_schema_modules;

/// Generate a schema snapshot from all schema modules.
///
/// Returns the combined SQL content. `extract_definitions()` is used to validate
/// parseability and produce the summary comment.
///
/// For embedded manifests, uses pre-loaded modules directly.
///
/// TODO(v0.2): produce a true merged snapshot — currently this concatenates
/// modules in dependency order, so OVERWRITE definitions appear alongside the
/// originals rather than showing the final resolved state.
pub fn generate(manifest: &Manifest) -> crate::Result<String> {
	let modules = match &manifest.preloaded_modules {
		Some(m) => m.clone(),
		None => load_schema_modules(manifest)?,
	};

	let mut all_sql = String::new();
	for m in &modules {
		all_sql.push_str(&m.content);
		all_sql.push('\n');
	}

	if all_sql.trim().is_empty() {
		return Ok(String::from(
			"-- Auto-generated by overshift. No schema modules found.\n",
		));
	}

	// Validate that the SQL is parseable
	let defs = surql_parser::extract_definitions(&all_sql)
		.map_err(|e| Error::Snapshot(format!("failed to parse schema SQL: {e}")))?;

	let mut output = String::from("-- Auto-generated by overshift. Do not edit.\n\n");

	for module in &modules {
		output.push_str(&format!("-- Module: {}\n", module.name));
		output.push_str(&module.content);
		output.push_str("\n\n");
	}

	// Append summary comment
	output.push_str(&format!(
		"-- Summary: {} tables, {} fields, {} indexes, {} functions, {} analyzers\n",
		defs.tables.len(),
		defs.fields.len(),
		defs.indexes.len(),
		defs.functions.len(),
		defs.analyzers.len(),
	));

	Ok(output)
}

/// Check if the existing snapshot matches the current state.
///
/// Returns `true` if `generated/current.surql` is up to date.
/// Requires a filesystem-based manifest.
pub fn check(manifest: &Manifest) -> crate::Result<bool> {
	let expected = generate(manifest)?;
	let snapshot_path = manifest.generated_dir()?.join("current.surql");

	if !snapshot_path.exists() {
		return Ok(false);
	}

	let actual = std::fs::read_to_string(&snapshot_path)
		.map_err(|e| Error::Snapshot(format!("failed to read {}: {e}", snapshot_path.display())))?;

	Ok(expected.trim() == actual.trim())
}

/// Write the snapshot to `generated/current.surql`.
///
/// Requires a filesystem-based manifest.
pub fn write(manifest: &Manifest) -> crate::Result<()> {
	let content = generate(manifest)?;
	let generated_dir = manifest.generated_dir()?;

	std::fs::create_dir_all(&generated_dir).map_err(|e| {
		Error::Snapshot(format!("failed to create {}: {e}", generated_dir.display()))
	})?;

	let snapshot_path = generated_dir.join("current.surql");
	std::fs::write(&snapshot_path, &content).map_err(|e| {
		Error::Snapshot(format!("failed to write {}: {e}", snapshot_path.display()))
	})?;

	Ok(())
}
