using System.Text.Json;
using XFEEDToolBox.Core.Data;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Navigation;
internal static class NativeStationServiceAcceptance
{
public static async Task RunAsync(int pid,string maintenanceAddress,string reportPath)
{
if(File.Exists(reportPath)||File.Exists(reportPath+".submitted.json"))throw new IOException("Use a new evidence path; never resend an uncertain service order.");
var settings=ToolboxSettings.Load(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"XFEstudio","XFEEDToolBox","settings.json"));
await using var journal=new JournalMonitor(settings.JournalDirectory);await journal.StartAsync();var initial=journal.State;
if(!initial.InGame||!initial.Docked||initial.MarketId is not {} market||initial.CurrentShipId!=7||!initial.StationServicesKnown||!initial.StationServices.Contains("refuel"))
throw new InvalidOperationException("Expected identified docked acceptance ship and a station providing refuel.");
var limpets=initial.Inventory.Where(x=>x.Key=="drones").Sum(x=>x.Count);
var testCargo=initial.Inventory.Where(x=>x.Key=="biowaste").Sum(x=>x.Count);
if(!initial.CargoReliable||initial.CargoCount!=16||!(limpets==16&&testCargo==0||limpets==15&&testCargo==1))
throw new InvalidOperationException("Expected the agreed test cargo or the restored 16-limpet baseline.");
using var stop=new CancellationTokenSource(TimeSpan.FromSeconds(100));
await using var protection=await FlightTestProtectionScope.ArmAsync(pid);var pipe=new NavigationPipeClient(pid);
async Task<JsonElement> Wait(ulong generation)
{
while(true)
{
var value=await pipe.CallAsync(new{op="commerce.market.status",generation},stop.Token);
if(value.GetProperty("state").GetInt32()>=3)return value;
await Task.Delay(80,stop.Token);
}
}
async Task<JsonElement> Prices()
{
var queued=await pipe.CallAsync(new{op="station.services.inspect",marketId=(uint)market,maintenanceAddress},stop.Token);
var result=await Wait(queued.GetProperty("generation").GetUInt64());
if(result.GetProperty("state").GetInt32()!=3)throw new InvalidOperationException("Original service quote failed.");return result;
}
var started=DateTimeOffset.UtcNow;var key=Guid.NewGuid().ToString("N");string? failure=null;
JsonElement? before=null,quote=null,receipt=null,after=null,serverAfter=null;
try
{
before=await pipe.CallAsync(new{op="station.services.inventory"},stop.Token);quote=await Prices();
var cost=quote.Value.GetProperty("fuelPrices").GetProperty("costToRefuel").GetInt64();
if(cost is <=0 or >50000)throw new InvalidOperationException("Expected a nonzero bounded original refuel quote.");
if(!journal.State.InGame||!journal.State.Docked||journal.State.MarketId!=market||journal.State.CurrentShipId!=initial.CurrentShipId||journal.State.LoadGameAt!=initial.LoadGameAt)
throw new InvalidOperationException("Docking or session changed before refuel.");
await File.WriteAllTextAsync(reportPath+".submitted.json",JsonSerializer.Serialize(new{key,market,pid,started,before,quotedCost=cost,action="refuel",instruction="Do not resend; query/reconcile this original operation."},ToolboxSettings.JsonOptions));
var queued=await pipe.CallAsync(new{op="station.services",marketId=(uint)market,maintenanceAddress,action="refuel",operationKey=key},stop.Token);
receipt=await Wait(queued.GetProperty("generation").GetUInt64());
if(receipt.Value.GetProperty("state").GetInt32()!=3||!receipt.Value.GetProperty("clientApplied").GetBoolean())throw new InvalidOperationException("Original refuel receipt unconfirmed; do not resend.");
after=await pipe.CallAsync(new{op="station.services.inventory"},stop.Token);
if(after.Value.GetProperty("credits").GetInt64()!=before.Value.GetProperty("credits").GetInt64()-cost||after.Value.GetProperty("cargoCount").GetInt32()!=before.Value.GetProperty("cargoCount").GetInt32()
||Math.Abs(after.Value.GetProperty("fuelLevel").GetDouble()-after.Value.GetProperty("fuelCapacity").GetDouble())>.01
||Math.Abs(after.Value.GetProperty("reserveLevel").GetDouble()-after.Value.GetProperty("reserveCapacity").GetDouble())>.01)
throw new InvalidOperationException("Actual native fuel, cargo or wallet disagrees with the service receipt.");
serverAfter=await Prices();
if(serverAfter.Value.GetProperty("fuelPrices").GetProperty("costToRefuel").GetInt64()!=0)throw new InvalidOperationException("A second original server quote did not confirm full fuel.");
}
catch(Exception error){failure=error.ToString();}
var passed=failure is null;
await File.WriteAllTextAsync(reportPath,JsonSerializer.Serialize(new{pid,market,started,finished=DateTimeOffset.UtcNow,key,passed,before,quote,receipt,after,serverAfter,failure,screenUse=false,osInputsSent=0,journalEventsSynthesized=0},ToolboxSettings.JsonOptions));
Console.WriteLine(JsonSerializer.Serialize(new{passed,market,failure,screenUse=false,journalEventsSynthesized=0}));
if(!passed)throw new InvalidOperationException("Original station service acceptance did not pass.");
}
}
using System.Text.Json;
using XFEEDToolBox.Core.Data;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Navigation;
internal static class NativeStationServiceAcceptance
{
public static async Task RunAsync(int pid,string maintenanceAddress,string reportPath)
{
if(File.Exists(reportPath)||File.Exists(reportPath+".submitted.json"))throw new IOException("Use a new evidence path; never resend an uncertain service order.");
var settings=ToolboxSettings.Load(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"XFEstudio","XFEEDToolBox","settings.json"));
await using var journal=new JournalMonitor(settings.JournalDirectory);await journal.StartAsync();var initial=journal.State;
if(!initial.InGame||!initial.Docked||initial.MarketId is not {} market||initial.CurrentShipId!=7||!initial.StationServicesKnown||!initial.StationServices.Contains("refuel"))
throw new InvalidOperationException("Expected identified docked acceptance ship and a station providing refuel.");
var limpets=initial.Inventory.Where(x=>x.Key=="drones").Sum(x=>x.Count);
var testCargo=initial.Inventory.Where(x=>x.Key=="biowaste").Sum(x=>x.Count);
if(!initial.CargoReliable||initial.CargoCount!=16||!(limpets==16&&testCargo==0||limpets==15&&testCargo==1))
throw new InvalidOperationException("Expected the agreed test cargo or the restored 16-limpet baseline.");
using var stop=new CancellationTokenSource(TimeSpan.FromSeconds(100));
await using var protection=await FlightTestProtectionScope.ArmAsync(pid);var pipe=new NavigationPipeClient(pid);
async Task<JsonElement> Wait(ulong generation)
{
while(true)
{
var value=await pipe.CallAsync(new{op="commerce.market.status",generation},stop.Token);
if(value.GetProperty("state").GetInt32()>=3)return value;
await Task.Delay(80,stop.Token);
}
}
async Task<JsonElement> Prices()
{
var queued=await pipe.CallAsync(new{op="station.services.inspect",marketId=(uint)market,maintenanceAddress},stop.Token);
var result=await Wait(queued.GetProperty("generation").GetUInt64());
if(result.GetProperty("state").GetInt32()!=3)throw new InvalidOperationException("Original service quote failed.");return result;
}
var started=DateTimeOffset.UtcNow;var key=Guid.NewGuid().ToString("N");string? failure=null;
JsonElement? before=null,quote=null,receipt=null,after=null,serverAfter=null;
try
{
before=await pipe.CallAsync(new{op="station.services.inventory"},stop.Token);quote=await Prices();
var cost=quote.Value.GetProperty("fuelPrices").GetProperty("costToRefuel").GetInt64();
if(cost is <=0 or >50000)throw new InvalidOperationException("Expected a nonzero bounded original refuel quote.");
if(!journal.State.InGame||!journal.State.Docked||journal.State.MarketId!=market||journal.State.CurrentShipId!=initial.CurrentShipId||journal.State.LoadGameAt!=initial.LoadGameAt)
throw new InvalidOperationException("Docking or session changed before refuel.");
await File.WriteAllTextAsync(reportPath+".submitted.json",JsonSerializer.Serialize(new{key,market,pid,started,before,quotedCost=cost,action="refuel",instruction="Do not resend; query/reconcile this original operation."},ToolboxSettings.JsonOptions));
var queued=await pipe.CallAsync(new{op="station.services",marketId=(uint)market,maintenanceAddress,action="refuel",operationKey=key},stop.Token);
receipt=await Wait(queued.GetProperty("generation").GetUInt64());
if(receipt.Value.GetProperty("state").GetInt32()!=3||!receipt.Value.GetProperty("clientApplied").GetBoolean())throw new InvalidOperationException("Original refuel receipt unconfirmed; do not resend.");
after=await pipe.CallAsync(new{op="station.services.inventory"},stop.Token);
if(after.Value.GetProperty("credits").GetInt64()!=before.Value.GetProperty("credits").GetInt64()-cost||after.Value.GetProperty("cargoCount").GetInt32()!=before.Value.GetProperty("cargoCount").GetInt32()
||Math.Abs(after.Value.GetProperty("fuelLevel").GetDouble()-after.Value.GetProperty("fuelCapacity").GetDouble())>.01
||Math.Abs(after.Value.GetProperty("reserveLevel").GetDouble()-after.Value.GetProperty("reserveCapacity").GetDouble())>.01)
throw new InvalidOperationException("Actual native fuel, cargo or wallet disagrees with the service receipt.");
serverAfter=await Prices();
if(serverAfter.Value.GetProperty("fuelPrices").GetProperty("costToRefuel").GetInt64()!=0)throw new InvalidOperationException("A second original server quote did not confirm full fuel.");
}
catch(Exception error){failure=error.ToString();}
var passed=failure is null;
await File.WriteAllTextAsync(reportPath,JsonSerializer.Serialize(new{pid,market,started,finished=DateTimeOffset.UtcNow,key,passed,before,quote,receipt,after,serverAfter,failure,screenUse=false,osInputsSent=0,journalEventsSynthesized=0},ToolboxSettings.JsonOptions));
Console.WriteLine(JsonSerializer.Serialize(new{passed,market,failure,screenUse=false,journalEventsSynthesized=0}));
if(!passed)throw new InvalidOperationException("Original station service acceptance did not pass.");
}
}