using XFEEliteDangerous.Client;
using System.Text.Json;
using System.Text.Json.Nodes;
using XFEEliteDangerous.Core;
using XFEEliteDangerous.ManagementClient;
namespace XFEEliteDangerous.Desktop;
/// Management API adapter; the desktop never opens the server's state file.
public sealed class LocalClient : IDisposable
{
private ManagementApi? api;
private string? address;
public string? Token {get;set;}
private static JsonObject Input(object value)=>JsonSerializer.SerializeToNode(value,Wire.Json)!.AsObject();
public Task StatusAsync(CancellationToken cancel=default)=>Call("status",null,cancel);
public Task LoginAsync(string email,string password,CancellationToken cancel=default)=>Call("auth/login",new{email,password},cancel);
public Task RegisterAsync(string email,string name,string password,CancellationToken cancel=default)=>Call("auth/register",new{email,name,password},cancel);
public Task ProfileAsync(CancellationToken cancel=default)=>Call("profile",null,cancel);
public Task AccountsAsync(CancellationToken cancel=default)=>Call("accounts",null,cancel);
public Task PermissionsAsync(CancellationToken cancel=default)=>Call("permissions",null,cancel);
public Task SettingsAsync(CancellationToken cancel=default)=>Call("settings",null,cancel);
public Task GalaxyAsync(CancellationToken cancel=default)=>Call("galaxy",null,cancel);
public Task EditGalaxyAsync(string version,string kind,string key,string payload,CancellationToken cancel=default)=>Call("galaxy/edit",new{version,kind,key,payload},cancel);
public Task CommanderAsync(string? id=null,CancellationToken cancel=default)=>Call("commander",new{accountId=id},cancel);
public Task CatalogAsync(string search="",CancellationToken cancel=default,string? accountId=null)=>Call("catalog",new{search,accountId},cancel);
public Task EditAsync(string? id,InventoryEdit operation,object values,string version,CancellationToken cancel=default)=>Call("inventory/edit",new{accountId=id,edit=operation.ToString().ToLowerInvariant(),values,version},cancel);
public Task EditQuantityAsync(string? id,string category,string itemId,string itemCategory,long quantity,string version,CancellationToken cancel=default)
{
if(category is not("cargo" or "materials"))throw new ArgumentException(AppText.Get("LocalClient.Text286"));
return EditAsync(id,category=="cargo"?InventoryEdit.Cargo:InventoryEdit.Materials,new{itemId,category=itemCategory,quantity},version,cancel);
}
public Task ImportJournalAsync(string id,JsonObject commander,string version,CancellationToken cancel=default)=>Call("journal/import",new{accountId=id,commander,version},cancel);
public Task SetRoleAsync(string id,string role,CancellationToken cancel=default)=>Call("accounts/role",new{accountId=id,role},cancel);
public Task CreateAccountAsync(string email,string name,string password,string role,CancellationToken cancel=default)=>Call("accounts/create",new{email,name,password,role},cancel);
public Task UpdatePermissionsAsync(bool credits,bool cargo,bool materials,bool ships,CancellationToken cancel=default)=>Call("permissions/update",new{permissions=new Permissions{AllowPlayerCredits=credits,AllowPlayerCargo=cargo,AllowPlayerMaterials=materials,AllowPlayerShipEditing=ships}},cancel);
public Task UpdateSettingsAsync(string name,string gameDirectory,bool registration,CancellationToken cancel=default)=>Call("settings/update",new{name,gameDirectory,registration},cancel);
public Task FleetAsync(string? id,CancellationToken cancel=default)=>Call("fleet",new{accountId=id},cancel);
public Task EditFleetAsync(string? id,JsonObject edit,string version,CancellationToken cancel=default)
{var input=(JsonObject)edit.DeepClone();input["accountId"]=id;input["version"]=version;return Call("fleet/edit",input,cancel);}
public Task PrepareMaterialsAsync(string? id,string version,CancellationToken cancel=default)=>Call("materials/prepare",new{accountId=id,version},cancel);
public Task ExportNativeAsync(string? id,CancellationToken cancel=default)=>Call("native/export",new{accountId=id},cancel);
public Task InitializeNativeAsync(string? id,string version,CancellationToken cancel=default)=>Call("native/initialize",new{accountId=id,version},cancel);
public Task ImportNativeAsync(string? id,JsonObject save,string version,CancellationToken cancel=default)=>Call("native/import",new{accountId=id,save,version},cancel);
public async Task ConnectLocalOwnerAsync()
{
var path=Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"XFEEliteDangerous","Server","admin-access.json");
if(!File.Exists(path))return false;
var access=JsonNode.Parse(await File.ReadAllTextAsync(path))!;
SystemProfile.ServerAddress=access["address"]!.GetValue();
Token=access["key"]!.GetValue();
try
{
var result=await Call("auth/local-owner",null,default);
Token=result["token"]!.GetValue();DesktopContracts.ApplyPlayerAccount(result["account"]!);DesktopContext.State.IsLoggedIn=true;
await DesktopContext.RefreshPermissionsAsync();return true;
}
catch{Token=null;throw;}
}
public void Logout()
{
var previous=Token;Token=null;
if(previous is not null)_=LogoutServerAsync(previous);
}
private async Task LogoutServerAsync(string token)
{try{if(api is not null)await api.CallAsync("auth/logout",token:token);}catch(Exception error){DesktopContext.Log(error);}}
private async Task Call(string operation,object? values,CancellationToken cancel)
{
if(DesktopVerification.IsRunning)throw new InvalidOperationException(AppText.Get("LocalClient.Text287"));
if(api is null||address!=SystemProfile.ServerAddress)
{api?.Dispose();address=SystemProfile.ServerAddress;api=new(address);}
var session=Token;
try{return await api.CallAsync(operation,values is JsonObject json?json:values is null?null:Input(values),session,cancel).ConfigureAwait(false);}
catch(LocalSessionException){await DesktopContext.InvalidateSessionAsync(session).ConfigureAwait(false);throw;}
}
public void Dispose()=>api?.Dispose();
}