using System.Text.Json;
using System.Text.RegularExpressions;
using Repoverse.Core.Model;
using Repoverse.Core.Voxels;
namespace Repoverse.Core.Bridge;
public sealed record BuildOrigin(int X,int Y,int Z);
public sealed record BuildOperation(string Name,JsonElement Args);
public sealed record BuilderPlan(int SchemaVersion,string Id,string Actor,string Units,BuildOrigin Origin,BuildOperation[] Operations);
public sealed record BuilderState(string Actor,string PlanId,double X,double Y,double Z,int Voxels);
public readonly record struct BuildVoxel(int X,int Y,int Z,ushort Packed);
/// Data-only Twin builder contract. Validate and rasterize completely before editing the world.
public static class BuilderRasterizer
{
public const int MaxVoxels=32768;
public static IReadOnlyList Rasterize(BuilderPlan plan)
{
if(plan.SchemaVersion!=1 || plan.Units!="voxel" || string.IsNullOrWhiteSpace(plan.Id) || plan.Id.Length>128 || string.IsNullOrWhiteSpace(plan.Actor) || plan.Actor.Length>64
|| plan.Origin is null || plan.Operations is null || plan.Operations.Length is <1 or >128)throw new ArgumentException("Invalid builder plan.");
if(Math.Abs((long)plan.Origin.X)>1_000_000 || Math.Abs((long)plan.Origin.Z)>1_000_000)throw new ArgumentException("Invalid build origin.");
var result=new Dictionary<(int,int,int),BuildVoxel>();long candidates=0;
foreach(var operation in plan.Operations)
{
if(operation is null || operation.Args.ValueKind!=JsonValueKind.Object)throw new ArgumentException("Invalid build operation.");
var a=operation.Args;
double N(string key,double fallback,double min,double max) {
double v=fallback;
if(a.TryGetProperty(key,out var p) && (p.ValueKind!=JsonValueKind.Number || !p.TryGetDouble(out v)))throw new ArgumentException("Invalid "+key);
if(!double.IsFinite(v)||vmax)throw new ArgumentException("Invalid "+key);return v;
}
double cx=plan.Origin.X+N("x",0,-256,256), cy=plan.Origin.Y+N("y",1,-256,256), cz=plan.Origin.Z+N("z",0,-256,256);
double rx,ry,rz,r=0,tube=0,h=0;
switch(operation.Name) {
case "add_box":rx=N("width",1,.02,64)/2;ry=N("height",1,.02,64)/2;rz=N("depth",1,.02,64)/2;break;
case "add_sphere":rx=ry=rz=r=N("radius",.7,.02,64);break;
case "add_cylinder":rx=rz=r=N("radius",.5,.02,64);ry=(h=N("height",2,.02,64))/2;break;
case "add_cone":rx=rz=r=N("radius",.6,.02,64);ry=(h=N("height",1.5,.02,64))/2;break;
case "add_torus":r=N("radius",1,.02,64);tube=N("tube",.3,.02,64);if(tube>r)throw new ArgumentException("Invalid torus tube");rx=ry=r+tube;rz=tube;break;
default:throw new ArgumentException("Unsupported build shape: "+operation.Name);
}
// Sample voxel centers in the same Y-up coordinates as the preview. Torus lies in XY.
int xmin=(int)Math.Ceiling(cx-rx-.5),xmax=(int)Math.Floor(cx+rx-.5),ymin=(int)Math.Ceiling(cy-ry-.5),ymax=(int)Math.Floor(cy+ry-.5),zmin=(int)Math.Ceiling(cz-rz-.5),zmax=(int)Math.Floor(cz+rz-.5);
if(cx-rx < -1_000_000 || cx+rx>1_000_000 || cz-rz < -1_000_000 || cz+rz>1_000_000 || cy-ry<1 || cy+ry>(WorldGenerator.MaxChunkY+1)*WorldConstants.ChunkSize)throw new ArgumentException("Build exceeds world bounds.");
candidates+=(long)Math.Max(0,xmax-xmin+1)*Math.Max(0,ymax-ymin+1)*Math.Max(0,zmax-zmin+1);
if(candidates>262144)throw new ArgumentException("Build sampling budget exceeded.");
var color=a.TryGetProperty("color",out var c)?(c.ValueKind==JsonValueKind.String?c.GetString():null):"#5ad1c4";
if(color is null || !Regex.IsMatch(color,"^#[0-9a-fA-F]{6}$"))throw new ArgumentException("Invalid build color.");
// Quantize to the existing ten material families; save schema retains packed voxel materials.
int red=Convert.ToInt32(color.Substring(1,2),16),green=Convert.ToInt32(color.Substring(3,2),16),blue=Convert.ToInt32(color.Substring(5,2),16);
var material=red>green&&red>blue?MaterialFamily.Ceramic:green>red&&green>blue?MaterialFamily.Wood:blue>red?MaterialFamily.Glass:MaterialFamily.Concrete;
ushort packed=new Voxel(BlockType.Wall,material).Packed;
for(int x=xmin;x<=xmax;x++)for(int y=ymin;y<=ymax;y++)for(int z=zmin;z<=zmax;z++) {
double dx=x+.5-cx,dy=y+.5-cy,dz=z+.5-cz;
bool inside=operation.Name switch {
"add_sphere"=>dx*dx+dy*dy+dz*dz<=r*r,
"add_cylinder"=>dx*dx+dz*dz<=r*r,
"add_cone"=>dx*dx+dz*dz<=Math.Pow(r*(.5-dy/h),2),
"add_torus"=>Math.Pow(Math.Sqrt(dx*dx+dy*dy)-r,2)+dz*dz<=tube*tube,
_=>true
};
if(!inside)continue;result[(x,y,z)]=new(x,y,z,packed);
if(result.Count>MaxVoxels)throw new ArgumentException("Build voxel budget exceeded.");
}
}
if(result.Count==0)throw new ArgumentException("Shapes are too small for this voxel grid. Increase their size.");
return result.Values.ToArray();
}
}