#include "HyperTwistIPC/HyperTwistSolverOracleLibrary.h"
#include "HyperTwistSolverLibrary.h"
#include "Misc/Paths.h"
#include "Misc/FileHelper.h"
#include "HAL/PlatformFilemanager.h"

FString UHyperTwistSolverOracleLibrary::GetBrownanSolverPath()
{
    return FPaths::ConvertRelativePathToFull(
        FPaths::ProjectDir() / TEXT("Binaries/Win64/Solvers/brownan-solver.exe"));
}

FString UHyperTwistSolverOracleLibrary::GetCahidenesWrapperPath()
{
    return FPaths::ConvertRelativePathToFull(
        FPaths::ProjectDir() / TEXT("Binaries/Win64/Solvers/cahidenes_solver_wrapper.py"));
}

bool UHyperTwistSolverOracleLibrary::AreSolverOraclesAvailable()
{
    return FPaths::FileExists(GetBrownanSolverPath())
        && FPaths::FileExists(GetCahidenesWrapperPath());
}

bool UHyperTwistSolverOracleLibrary::VerifySolutionWithBrownanOracle(const FString& CubeState, const TArray<FString>& Moves)
{
    FString SolverPath = GetBrownanSolverPath();
    if (!FPaths::FileExists(SolverPath))
    {
        UE_LOG(LogTemp, Warning, TEXT("Brownan solver not found at %s"), *SolverPath);
        return false;
    }

    UHyperTwistIPCProcessManager* IPC = UHyperTwistIPCProcessManager::CreateIPCManager(
        GetTransientPackage());

    FHyperTwistIPCProcessOptions Options;
    Options.ExecutablePath = SolverPath;
    Options.Arguments.Add(CubeState);
    Options.TimeoutSeconds = 60.0f;

    FHyperTwistIPCResult Result = IPC->RunProcess(Options);
    if (!Result.bSuccess)
    {
        UE_LOG(LogTemp, Warning, TEXT("Brownan solver failed: %s"), *Result.ErrorMessage);
        return false;
    }

    // brownan solver outputs solution moves to stdout; verify against provided moves
    FString Output = Result.StdOut.TrimStartAndEnd();
    FString Expected = FString::Join(Moves, TEXT(" ")).TrimStartAndEnd();
    return Output.Equals(Expected, ESearchCase::IgnoreCase);
}

TArray<FString> UHyperTwistSolverOracleLibrary::SolveWithCahidenesOracle(const FString& FaceletString)
{
    TArray<FString> EmptyResult;
    FString WrapperPath = GetCahidenesWrapperPath();
    if (!FPaths::FileExists(WrapperPath))
    {
        UE_LOG(LogTemp, Warning, TEXT("Cahidenes wrapper not found at %s"), *WrapperPath);
        return EmptyResult;
    }

    UHyperTwistIPCProcessManager* IPC = UHyperTwistIPCProcessManager::CreateIPCManager(
        GetTransientPackage());

    FHyperTwistIPCProcessOptions Options;
    Options.ExecutablePath = TEXT("python.exe");
    Options.Arguments.Add(WrapperPath);
    Options.Arguments.Add(FaceletString);
    Options.TimeoutSeconds = 30.0f;

    FHyperTwistIPCResult Result = IPC->RunProcess(Options);
    if (!Result.bSuccess)
    {
        UE_LOG(LogTemp, Warning, TEXT("Cahidenes solver failed: %s"), *Result.ErrorMessage);
        return EmptyResult;
    }

    FString Output = Result.StdOut.TrimStartAndEnd();
    TArray<FString> Moves;
    Output.ParseIntoArrayWS(Moves);
    return Moves;
}

bool UHyperTwistSolverOracleLibrary::CompareSolvers(const FString& FaceletString)
{
    // rob-twophase result
    TArray<FString> RobTwophaseMoves = UHyperTwistSolverLibrary::SolveClassicState(FaceletString, 5000, 25, 1);
    TArray<FString> RobMoves;
    RobTwophaseSolution.ParseIntoArrayWS(RobMoves);

    // cahidenes result
    TArray<FString> CahidenesMoves = SolveWithCahidenesOracle(FaceletString);

    return RobMoves.Num() == CahidenesMoves.Num();
}
