using System.Diagnostics;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Navigation;
namespace XFEEDToolBox.Core.Session;
/// Enters the selected original mode; never edits account or menu availability fields.
public sealed class NativeSessionEntryWorkflow(INativeRelogChannel channel,Func player,string mode,string commander)
{
public TimeSpan Timeout{get;init;}=TimeSpan.FromMinutes(8);
public TimeSpan PollInterval{get;init;}=TimeSpan.FromMilliseconds(60);
public TimeSpan ConnectionFailureConfirmation{get;init;}=TimeSpan.FromSeconds(2);
public DateTimeOffset? ProcessStartedAt{get;init;}
public long? ExpectedShipId{get;init;}
public event Action? Progress;
public async Task RunAsync(CancellationToken token)
{
if(!NativeRelogWorkflow.SupportsMode(mode))throw new NotSupportedException("自动恢复只支持已接入的单人或公开模式。");
var owner=Guid.NewGuid().ToString("N");var watch=Stopwatch.StartNew();string? request=null;var initial=player().LoadGameAt;
var attempted=false;string? lastState=null;
var failed=new NativeConnectionFailureObserver{ConfirmationPeriod=ConnectionFailureConfirmation};
void RequireIdentity()
{
token.ThrowIfCancellationRequested();var current=player();
if(commander.Length>0&¤t.CommanderId.Length>0&¤t.CommanderId!=commander
||ExpectedShipId is {} ship&¤t.CurrentShipId is {} actual&&actual!=ship
||current.GameMode.Length>0&¤t.GameMode!=mode&¤t.LoadGameAt is {} loaded
&&(ProcessStartedAt is {} started?loaded>=started:initial is null||loaded>initial))
throw new InvalidDataException("原始载入期间指挥官或飞船身份改变,未自动重启。");
}
try
{
attempted=true;var begun=false;
Progress?.Invoke("正在等待游戏原始主菜单就绪");
while(watch.Elapsed0&&(ProcessStartedAt is not null||initial is null||current.LoadGameAt>initial))return;
}
try{await channel.BeginAsync(owner,token).ConfigureAwait(false);begun=true;break;}
catch(InvalidOperationException error)when(error.Message.StartsWith("Wait for an idle game and stop automatic flight before relogging",StringComparison.Ordinal)){}
catch(Exception error)when(!token.IsCancellationRequested&&!NavigationRecovery.HasInvalidDataCause(error)&&error is IOException or OperationCanceledException){}
await Task.Delay(PollInterval,token).ConfigureAwait(false);
}
if(!begun)throw new TimeoutException("游戏原始主菜单未就绪,当前客户端已保留。");
while(watch.ElapsedTimeSpan.FromMilliseconds(350)||ageinitial)
&¤t.GameMode==mode&¤t.CommanderId.Length>0)
{
if(commander.Length>0&¤t.CommanderId!=commander)throw new InvalidOperationException("重新进入的指挥官与原会话不同。");
return;
}
if(request is not null)
{
if(frame.LastRequestId==request&&(frame.ActionApplied||frame.ActionFailed))
{if(frame.ActionFailed)throw new IOException("原始游戏模式选择失败,未重放选择。");request=null;}
else{await Task.Delay(PollInterval,token);continue;}
}
var action=frame.Stable?frame.State switch{"MainMenu"=>"ContinueMenu","ContinueMenu"=>mode=="Solo"?"SetupPlaySolo":"SetupPlayWithEveryone",_=>null}:null;
if(action is not null&&frame.Actions.Count(a=>a.Enabled&&a.Name==action)==1)
{
request=Guid.NewGuid().ToString("N");
try{await channel.SubmitAsync(owner,request,frame,action,token).ConfigureAwait(false);}
catch(NativeSessionStateChangedException){request=null;}
catch(IOException error)when(!NavigationRecovery.HasInvalidDataCause(error)){}catch(OperationCanceledException)when(!token.IsCancellationRequested){}
}
await Task.Delay(PollInterval,token).ConfigureAwait(false);
}
throw new TimeoutException("游戏原始载入未完成,当前客户端与日志已保留。");
}
finally{if(attempted)await channel.EndAsync(owner).ConfigureAwait(false);}
}
}