#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "HyperTwistRecognition/HyperTwistRecognitionTypes.h"
#include "HyperTwistSimulation/HyperTwistClassicCubeLeaderboardLibrary.h"
#include "HyperTwistSimulation/HyperTwistClassicCubeOperatorSurface.h"
#include "HyperTwistSimulation/HyperTwistClassicCubeTypes.h"
#include "HyperTwistTraining/HyperTwistTrainingTypes.h"
#include "HyperTwistClassicCubeGameMode.generated.h"

class AHyperTwistClassicCubeActor;
class AHyperTwistClassicCubeOrbitPawn;
class AHyperTwistClassicCubePlayerController;
class IVoiceCapture;
class UAudioComponent;
class UHyperTwistClassicCubeHUDWidget;
class UHyperTwistPuzzleViewerComponent;
class USoundWaveProcedural;
class UHyperTwistTrainingSubsystem;

UCLASS(BlueprintType, Blueprintable)
class UNREALHYPERTWIST_API AHyperTwistClassicCubeGameMode
	: public AGameModeBase
	, public IHyperTwistClassicCubeOperatorSurface
{
	GENERATED_BODY()

public:
	AHyperTwistClassicCubeGameMode();

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|ClassicCube|HUD")
	bool bAutoCreateHud = true;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|ClassicCube|HUD")
	int32 HudZOrder = 0;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|ClassicCube|HUD")
	TSubclassOf<UHyperTwistClassicCubeHUDWidget> HudWidgetClass;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|ClassicCube")
	bool bAutoSpawnCubeActor = true;

	/** Recover a missing PlayerStart/default pawn with a possessed orbit camera. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|ClassicCube|Presentation")
	bool bEnsurePlayableOrbitPawn = true;

	/** Supply a first-party key light when a sparse or damaged map has none. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|ClassicCube|Presentation")
	bool bEnsureRuntimeLighting = true;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|ClassicCube")
	FVector CubeSpawnLocation = FVector::ZeroVector;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|ClassicCube")
	FRotator CubeSpawnRotation = FRotator::ZeroRotator;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|ClassicCube")
	int32 ScrambleLength = 20;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|ClassicCube|Training")
	FString FollowAlongScriptedScrambleNotation;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|ClassicCube")
	FString DefaultUserId = TEXT("local-user");

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|ClassicCube")
	FString SessionIdPrefix = TEXT("classic_cube");

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|ClassicCube|Training")
	EHyperTwistClassicCubeSessionMode DefaultSessionMode =
		EHyperTwistClassicCubeSessionMode::FreePlay;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|ClassicCube|Solver")
	bool bShowFullSolutionSequence = true;

	/** Search budget used by the non-blocking classic-cube guidance worker. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|ClassicCube|Solver", meta = (ClampMin = "1"))
	int32 SolverTimeLimitMs = 1500;

	/** Maximum notation length accepted from the classic-cube guidance worker. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|ClassicCube|Solver", meta = (ClampMin = "1"))
	int32 SolverMaximumMoveCount = 32;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|ClassicCube|Speech")
	bool bEnableVoiceCommandCapture = true;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|ClassicCube|Speech")
	bool bUseMockSpeechClient = false;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|ClassicCube|Speech")
	bool bEnableCoachNarration = true;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|ClassicCube|Speech")
	FString SelectedVoiceProfileId = TEXT("en_US-lessac-medium");

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|ClassicCube|Speech")
	float NarrationVolumeMultiplier = 1.0f;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|ClassicCube")
	TObjectPtr<AHyperTwistClassicCubeActor> ActiveCubeActor = nullptr;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|ClassicCube|Presentation")
	TObjectPtr<AHyperTwistClassicCubeOrbitPawn> ActiveOrbitPawn = nullptr;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|ClassicCube|HUD")
	TObjectPtr<UHyperTwistClassicCubeHUDWidget> ActiveHudWidget = nullptr;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|ClassicCube|Training")
	EHyperTwistClassicCubeSessionMode ActiveSessionMode =
		EHyperTwistClassicCubeSessionMode::FreePlay;

	UFUNCTION(BlueprintCallable, Category = "HyperTwist|ClassicCube")
	void StartFreshAttempt();

	UFUNCTION(BlueprintCallable, Category = "HyperTwist|ClassicCube|Solver")
	void RequestHint();

	UFUNCTION(BlueprintCallable, Category = "HyperTwist|ClassicCube|Training")
	void SubmitCurrentSolve();

	UFUNCTION(BlueprintCallable, Category = "HyperTwist|ClassicCube|Training")
	void ToggleFollowAlongMode();

	UFUNCTION(BlueprintCallable, Category = "HyperTwist|ClassicCube|Replay")
	bool ExportActiveReplayToDefaultPath(FString& OutResolvedPath);

	UFUNCTION(BlueprintCallable, Category = "HyperTwist|ClassicCube|Replay")
	bool LoadReplayFromFile(const FString& ReplayPath, FString& OutResolvedPath);

	UFUNCTION(BlueprintCallable, Category = "HyperTwist|ClassicCube|Speech")
	bool BeginVoiceCommandCapture();

	UFUNCTION(BlueprintCallable, Category = "HyperTwist|ClassicCube|Speech")
	void EndVoiceCommandCapture();

	UFUNCTION(BlueprintCallable, Category = "HyperTwist|ClassicCube|Speech")
	void CycleVoiceProfile();

	UFUNCTION(BlueprintPure, Category = "HyperTwist|ClassicCube|Input")
	bool CanAcceptGameplayMoveInput() const;

	UFUNCTION(BlueprintPure, Category = "HyperTwist|ClassicCube|Speech")
	bool IsVoiceCommandCaptureActive() const;

protected:
	virtual void BeginPlay() override;
	virtual void Tick(float DeltaSeconds) override;
	virtual void RequestClassicCubeFreshAttempt_Implementation() override;
	virtual void RequestClassicCubeHint_Implementation() override;
	virtual void RequestClassicCubeSubmitSolve_Implementation() override;
	virtual void RequestClassicCubeToggleFollowAlongMode_Implementation() override;
	virtual void RequestClassicCubeBeginVoiceCommandCapture_Implementation() override;
	virtual void RequestClassicCubeEndVoiceCommandCapture_Implementation() override;
	virtual void RequestClassicCubeCycleVoiceProfile_Implementation() override;
	virtual bool CanClassicCubeAcceptGameplayMoveInput_Implementation() const override;
	virtual bool IsClassicCubeAnyActionButtonHovered_Implementation() const override;

	void ApplyLaunchOverridesFromCommandLine();
	void QueueReplayAutoExit(double CountdownSeconds);
	UHyperTwistTrainingSubsystem* ResolveTrainingSubsystem() const;
	AHyperTwistClassicCubeActor* ResolveOrSpawnCubeActor();
	AHyperTwistClassicCubeOrbitPawn* ResolveOrSpawnOrbitPawn();
	void EnsureRuntimeLighting();
	UHyperTwistClassicCubeHUDWidget* ResolveOrCreateHudWidget();
	void RefreshHud();
	void TickReplayPlayback(float DeltaSeconds);
	void TickReplayAutoExit(float DeltaSeconds);
	FString BuildSessionId() const;
	FString ResolveActivePuzzleId() const;
	void WriteActiveReplayMetadata();
	void CaptureInitialReplaySnapshotIfNeeded();
	bool TryBuildRuntimeReplaySnapshot(FHyperTwistStateSnapshot& OutSnapshot) const;
	void LoadLocalLeaderboardState();
	void RefreshLocalLeaderboardLine();
	void RecordSolvedAttemptToLocalLeaderboard();
	void RefreshSolverGuidance(bool bForceClearHint = false);
	void StartPendingSolverGuidanceTask();
	void CompleteSolverGuidanceTask(
		uint64 RequestSerial,
		const FString& FaceletString,
		bool bForceClearHint,
		TArray<FString>&& SolutionNotation
	);
	void RefreshVoiceProfiles();
	TArray<FString> ResolveScrambleMovesForCurrentSession() const;
	void ProcessCompletedMoveHistory();
	void HandleGameplayMoveDescriptor(const FHyperTwistClassicCubeMoveDescriptor& MoveDescriptor);
	void HandleFollowAlongMove(const FHyperTwistClassicCubeMoveDescriptor& MoveDescriptor);
	void FinalizeSolvedAttemptIfNeeded();
	bool IsCubeSettled() const;
	void QueueNarrationEvent(const FString& EventId, const FString& ScriptText);
	bool TryPlayNarrationResult(const FHyperTwistNarrationSynthesisResult& NarrationResult);
	void StopActiveNarration();
	void TickVoiceCommandCapture();
	bool DrainVoiceCaptureBuffer(FString& OutError);
	bool FinalizeVoiceCommandCaptureSubmission(FString& OutError);
	FString BuildVoiceCaptureFilePath(const FString& UtteranceId) const;
	bool WriteWaveFile(
		const FString& FilePath,
		const TArray<uint8>& PcmBytes,
		int32 SampleRateHz,
		int32 ChannelCount
	) const;
	bool TryExecuteVoiceCommand(const FHyperTwistClassicCubeVoiceCommand& VoiceCommand);
	void SetResultAndCoachLines(
		const FString& InResultLine,
		const FString& InHintLine,
		const FString& InModeLine,
		const FString& InVoiceLine
	);

private:
	UPROPERTY(Transient)
	TObjectPtr<UHyperTwistPuzzleViewerComponent> ReplayViewerComponent = nullptr;

	FHyperTwistTrainingLiveTimerState LastCompletedTimerState;
	FHyperTwistTrainingRunStepResult LastAttemptStepResult;
	FHyperTwistSpeechTranscriptResult LastVoiceTranscriptResult;
	FHyperTwistClassicCubeReplayMetadata ActiveReplayMetadata;
	FHyperTwistClassicCubeLeaderboardState LocalLeaderboardState;
	TArray<FString> ActiveSolutionNotation;
	TArray<FHyperTwistClassicCubeMoveDescriptor> ActiveSolutionQuarterTurns;
	TArray<FHyperTwistClassicCubeMoveDescriptor> FollowAlongGuideQuarterTurns;
	TArray<FHyperTwistClassicCubeMoveDescriptor> ReplayPlaybackMoves;
	TArray<FHyperTwistVoiceProfileSummary> AvailableVoiceProfiles;
	TArray<int32> ReplayPlaybackMoveTimesMs;
	TSharedPtr<IVoiceCapture> ActiveVoiceCapture;
	TArray<uint8> CapturedVoicePcmBytes;
	FString ActiveVoiceUtteranceId;
	TObjectPtr<UAudioComponent> ActiveNarrationAudioComponent = nullptr;
	FString ReplayLineOverride;
	FString LeaderboardLineOverride;
	FString LastReplayExportPath;
	FString LocalLeaderboardResolvedPath;
	FString LaunchOverrideReplayPath;
	FString ResultLineOverride;
	FString HintLineOverride;
	FString ModeLineOverride;
	FString VoiceLineOverride;
	FString PendingSolverFaceletString;
	FString ActiveSolverFaceletString;
	bool bAwaitingScrambleSettlement = false;
	bool bInspectionStarted = false;
	bool bSolveStartedFromGameplayMove = false;
	bool bAttemptComplete = false;
	bool bAttemptMarkedDnf = false;
	bool bInitialReplaySnapshotCaptured = false;
	bool bHasReplayMetadata = false;
	bool bReplayPlaybackActive = false;
	bool bLaunchOverrideDisableHud = false;
	bool bAutoQuitAfterReplayCompletion = false;
	bool bReplayAutoQuitPending = false;
	bool bVoiceCommandCaptureActive = false;
	bool bScrambleReadyNarrated = false;
	bool bSolveStartNarrated = false;
	bool bRuntimeLightingReady = false;
	bool bSolverGuidanceTaskActive = false;
	bool bPendingSolverForceClearHint = false;
	int32 ObservedCompletedMoveCount = 0;
	int32 FollowAlongStepIndex = 0;
	int32 FollowAlongCorrectMoveCount = 0;
	int32 FollowAlongIncorrectMoveCount = 0;
	int32 ReplayPlaybackNextMoveIndex = 0;
	int32 VoiceCaptureSampleRateHz = 16000;
	int32 VoiceCaptureChannelCount = 1;
	uint64 SolverGuidanceRequestSerial = 0;
	double ReplayPlaybackElapsedSeconds = 0.0;
	double ReplayAutoQuitTailSeconds = 1.5;
	double ReplayAutoQuitCountdownSeconds = -1.0;
	double VoiceCaptureStartedAtSeconds = 0.0;

	void RequestRuntimeReadyDiagnosticsCapture();
};

UCLASS(BlueprintType, Blueprintable)
class UNREALHYPERTWIST_API AHyperTwistClassicCubeFollowAlongGameMode
	: public AHyperTwistClassicCubeGameMode
{
	GENERATED_BODY()

public:
	AHyperTwistClassicCubeFollowAlongGameMode();
};
