using System;
using System.Threading;
using System.Threading.Tasks;
using JXHelper.Common;
using JXHelper.Config;
using JXHelper.Platform;
namespace JXHelper.Core;
///
/// Bộ não điều phối trung tâm vòng đời tự hành của Bot.
/// Tuân thủ bất biến: INV-FSM-SINGLE-OWNER & INV-SHM-TELEMETRY-SSOT.
///
public class BotBrain : IDisposable
{
private CancellationTokenSource? _cts;
private Task? _runnerTask;
private readonly SharedMemoryBridge _shm = new();
private readonly CombatEngine _combat = new();
private readonly RecoveryEngine _recovery = new();
private readonly ControlEngine _control = new();
private readonly PatrolEngine _patrol = new();
private IntPtr _activeHwnd = IntPtr.Zero;
private int _activePid;
private BotConfig? _currentConfig;
private string _lastActionText = "Khởi tạo";
public bool IsRunning { get; private set; }
public BotState CurrentState { get; private set; } = BotState.Idle;
public int CompletedCycles { get; private set; }
public event Action? OnLog;
public event Action? OnStateChanged;
public event Action? OnCountdownTick;
public BotBrain()
{
_combat.OnLog += Log;
_recovery.OnLog += Log;
_control.OnLog += Log;
_patrol.OnLog += Log;
}
private void SetState(BotState state, string actionName = "")
{
CurrentState = state;
if (!string.IsNullOrEmpty(actionName)) _lastActionText = actionName;
SyncShm(0);
TelemetryLogger.LogEvent(actionName.Length > 0 ? actionName : state.ToString(), state, _activePid, _lastActionText);
OnStateChanged?.Invoke(state);
}
private void Log(string message)
{
_lastActionText = message;
string timestamp = DateTime.Now.ToString("HH:mm:ss");
OnLog?.Invoke($"[{timestamp}] {message}");
TelemetryLogger.LogEvent("LOG", CurrentState, _activePid, message);
}
private void SyncShm(int countdownSec)
{
_shm.UpdateTelemetry(
CurrentState,
_activeHwnd,
_activePid,
CompletedCycles,
countdownSec,
_currentConfig?.RepairItemX ?? 0,
_currentConfig?.RepairItemY ?? 0,
_currentConfig?.TravelItemX ?? 0,
_currentConfig?.TravelItemY ?? 0,
_lastActionText
);
}
public void Start(IntPtr targetHwnd, BotConfig config)
{
if (IsRunning) return;
_activeHwnd = targetHwnd;
NativeMethods.GetWindowThreadProcessId(targetHwnd, out uint pid);
_activePid = (int)pid;
_currentConfig = config;
_cts = new CancellationTokenSource();
IsRunning = true;
SetState(BotState.Running, "Bắt đầu tự hành");
Log("Bắt đầu khởi chạy bộ não tự hành...");
// Khởi động các phân hệ tác chiến & phục hồi theo chuẩn VLBS
_combat.Start(targetHwnd, config);
_recovery.Start(targetHwnd, config);
_control.Start(targetHwnd, config);
_patrol.Start(targetHwnd, config);
_runnerTask = Task.Run(() => RunLoopAsync(targetHwnd, config, _cts.Token));
}
public void Stop()
{
if (!IsRunning) return;
_cts?.Cancel();
_combat.Stop();
_recovery.Stop();
_control.Stop();
_patrol.Stop();
IsRunning = false;
SetState(BotState.Idle, "Đã dừng");
Log("Đã dừng hoạt động của Bot.");
}
public async Task TriggerImmediateCycleAsync(IntPtr targetHwnd, BotConfig config)
{
if (CurrentState is BotState.ReturningToTown or BotState.OpeningBag or BotState.ClickingRepairItem)
{
Log("[CẢNH BÁO] Đang trong một chu kỳ khác, bỏ qua yêu cầu.");
return;
}
_activeHwnd = targetHwnd;
NativeMethods.GetWindowThreadProcessId(targetHwnd, out uint pid);
_activePid = (int)pid;
_currentConfig = config;
Log("[THỦ CÔNG] Kích hoạt chạy ngay một chu kỳ Về thành, Sửa đồ & Quay lại bãi...");
SetState(BotState.ReturningToTown, "Chu kỳ thủ công");
_combat.IsPaused = true;
_patrol.IsPaused = true;
using var tempCts = new CancellationTokenSource(TimeSpan.FromSeconds(45));
bool success = await ActionExecutor.ExecuteTownReturnAndRepairAsync(targetHwnd, config, Log, tempCts.Token);
if (success)
{
CompletedCycles++;
}
_combat.IsPaused = false;
_patrol.IsPaused = false;
SetState(IsRunning ? BotState.Running : BotState.Idle, "Hoàn tất chu kỳ thủ công");
}
private async Task RunLoopAsync(IntPtr targetHwnd, BotConfig config, CancellationToken ct)
{
try
{
while (!ct.IsCancellationRequested)
{
if (!config.PeriodicReturnEnabled || config.ReturnIntervalMinutes <= 0)
{
SyncShm(0);
await Task.Delay(1000, ct);
continue;
}
var interval = TimeSpan.FromMinutes(config.ReturnIntervalMinutes);
var targetTime = DateTime.UtcNow.Add(interval);
Log($"Đã lên lịch chu kỳ kế tiếp sau {config.ReturnIntervalMinutes} phút ({DateTime.Now.Add(interval):HH:mm:ss}).");
// Đếm ngược từng giây
while (DateTime.UtcNow < targetTime)
{
ct.ThrowIfCancellationRequested();
var remaining = targetTime - DateTime.UtcNow;
int remainingSec = (int)remaining.TotalSeconds;
SyncShm(remainingSec);
OnCountdownTick?.Invoke(remaining);
await Task.Delay(1000, ct);
}
// Kích hoạt chu kỳ
SetState(BotState.ReturningToTown, "Bắt đầu chu kỳ định kỳ");
_combat.IsPaused = true;
_patrol.IsPaused = true;
bool success = await ActionExecutor.ExecuteTownReturnAndRepairAsync(targetHwnd, config, Log, ct);
if (success)
{
CompletedCycles++;
SetState(BotState.FinishedCycle, "Hoàn tất chu kỳ an toàn");
}
_combat.IsPaused = false;
_patrol.IsPaused = false;
await Task.Delay(1000, ct);
SetState(BotState.Running, "Đang chờ chu kỳ kế tiếp");
}
}
catch (OperationCanceledException)
{
// Kết thúc êm đẹp khi người dùng bấm Dừng
}
catch (Exception ex)
{
Log($"[LỖI VÒNG LẶP] {ex.Message}");
SetState(BotState.Error, ex.Message);
}
finally
{
IsRunning = false;
}
}
public void Dispose()
{
Stop();
_shm.Dispose();
_control.Dispose();
_patrol.Dispose();
}
}