use cadrum::{Compound, Solid};
use glam::DVec3;

fn dvec3(x: f64, y: f64, z: f64) -> DVec3 {
	DVec3::new(x, y, z)
}

fn svg_string(shape: &[Solid], direction: DVec3, tol: f64) -> String {
	let mut buf = Vec::new();
	// Pick a sensible up for each view: Z-up for oblique/side views,
	// Y-up when looking straight down Z (view_dir ‖ Z would make Z-up degenerate).
	let up = if direction.normalize().dot(DVec3::Z).abs() > 0.999 { DVec3::Y } else { DVec3::Z };
	cadrum::mesh(shape, tol).and_then(|m| m.write_svg(direction, up, true, false, &mut buf)).unwrap();
	String::from_utf8(buf).unwrap()
}

#[test]
fn test_svg_box_isometric() {
	let shape = [Solid::cube(10.0, 10.0, 10.0)];
	let svg = svg_string(&shape, dvec3(1.0, 1.0, 1.0).normalize(), 0.1);

	assert!(svg.starts_with("<svg"), "should start with <svg tag");
	assert!(svg.contains("</svg>"), "should end with </svg>");
	assert!(svg.contains("<polyline"), "should contain polyline elements");
	assert!(svg.contains("viewBox"), "should contain viewBox");
	let svg_tag = &svg[..svg.find('>').unwrap()];
	assert!(!svg_tag.contains(" width="), "should not contain fixed width (responsive)");

	std::fs::create_dir_all("out").unwrap();
	std::fs::write("out/box_isometric.svg", &svg).unwrap();
	println!("SVG length: {} bytes", svg.len());
}

#[test]
fn test_svg_box_top_down() {
	let shape = [Solid::cube(10.0, 10.0, 10.0)];
	let svg = svg_string(&shape, DVec3::Z, 0.1);

	assert!(svg.starts_with("<svg"));
	assert!(svg.contains("<polyline"));

	std::fs::create_dir_all("out").unwrap();
	std::fs::write("out/box_top.svg", &svg).unwrap();
}

#[test]
fn test_svg_cylinder() {
	let shape = [Solid::cylinder(5.0, DVec3::Z, 10.0)];
	let svg = svg_string(&shape, dvec3(1.0, 0.5, 0.3).normalize(), 0.1);

	assert!(svg.contains("<polyline"));

	std::fs::create_dir_all("out").unwrap();
	std::fs::write("out/cylinder.svg", &svg).unwrap();
}

#[test]
fn test_svg_has_hidden_lines() {
	let a = [Solid::cube(10.0, 10.0, 10.0)];
	let b = [Solid::cube(10.0, 10.0, 10.0).translate(dvec3(5.0, 5.0, 0.0))];
	let shape: Vec<Solid> = a.union(&b).unwrap();
	let svg = svg_string(&shape, dvec3(1.0, 1.0, 1.0).normalize(), 0.1);

	assert!(svg.contains("#bbb"), "should contain hidden line color");

	std::fs::create_dir_all("out").unwrap();
	std::fs::write("out/two_boxes.svg", &svg).unwrap();
}

/// 球を+Xから描画したSVGと、Z軸180°回転後に+Xから描画したSVGで
/// polygon(面)の数が10%以上変わらないことを検証する。
/// 対称な球なので見え方はほぼ同じはず。
#[test]
fn test_svg_rotated_sphere_face_count_stable() {
	fn count_polygons(svg: &str) -> usize {
		svg.matches("<polygon ").count()
	}

	let shape = [Solid::sphere(5.0)];
	let svg_a = svg_string(&shape, DVec3::X, 0.1);
	let count_a = count_polygons(&svg_a);

	let rotated = shape.rotate_y(std::f64::consts::PI);
	let svg_b = svg_string(&rotated, DVec3::X, 0.1);
	let count_b = count_polygons(&svg_b);

	assert!(count_a > 0, "元のSVGにpolygonがない");
	assert!(count_b > 0, "回転後のSVGにpolygonがない");

	std::fs::create_dir_all("out").unwrap();
	std::fs::write("out/sphere_plus_x.svg", &svg_a).unwrap();
	std::fs::write("out/sphere_plus_x_rotated.svg", &svg_b).unwrap();

	let ratio = count_a as f64 / count_b as f64;
	assert!(
		(0.9..=1.1).contains(&ratio),
		"+X描画のpolygon数が回転前後で10%以上変化: {} → {} (ratio={:.3})",
		count_a, count_b, ratio
	);
}
