// Copyright (c) 2026 AccelByte Inc. All Rights Reserved.
// This is licensed software from AccelByte Inc, for limitations
// and restrictions contact your company contract manager.

#if WITH_EDITOR

#include "BlackBoxHelperInstaller.h"
#include "BlackBoxCommon.h"
#include "BlackBoxLog.h"
#include "Endpoints.h"

#include "CoreMinimal.h"
#include "Editor.h"
#include "HAL/PlatformFilemanager.h"
#include "HttpModule.h"
#include "Interfaces/IHttpResponse.h"
#include "Misc/CoreDelegates.h"
#include "Misc/FileHelper.h"
#include "Misc/Paths.h"
#include "Interfaces/IPluginManager.h"
#include "Async/Async.h"
#include "Misc/MessageDialog.h"
#include "UnrealEdMisc.h"


static const FString CRASH_HELPER_VERSION = TEXT("1.3.3");
static const FString ISSUE_REPORTER_VERSION = TEXT("1.2.1");

static FString CrashHelperDownloadURL =
    TEXT("https://cdn.prod.blackbox.accelbyte.io/sdk/helper/") +
    CRASH_HELPER_VERSION + TEXT("/blackbox_helper.exe");

static FString IssueReporterDownloadURL =
    TEXT("https://cdn.prod.blackbox.accelbyte.io/sdk/reporter/") +
    ISSUE_REPORTER_VERSION + TEXT("/blackbox_issue_reporter.exe");


FString GetCrashHelperPath()
{
    const TSharedPtr<IPlugin> Plugin =
    IPluginManager::Get().FindPlugin(TEXT("BlackBoxSDK"));

    check(Plugin.IsValid());

    return FPaths::Combine(
        Plugin->GetBaseDir(),
        TEXT("Source/BlackBoxSDK/Helper/x64/blackbox_helper.exe")
    );
}

FString GetIssueReporterPath()
{
    const TSharedPtr<IPlugin> Plugin =
    IPluginManager::Get().FindPlugin(TEXT("BlackBoxSDK"));

    check(Plugin.IsValid());

    return FPaths::Combine(
        Plugin->GetBaseDir(),
        TEXT("Source/BlackBoxSDK/Issue_Reporter/x64/blackbox_issue_reporter.exe")
    );
}

FString GetEnvFromBaseUrl(const FString& BaseUrl)
{
    FString Url = BaseUrl;
    Url.ToLowerInline();

    if (Url.Contains(TEXT("://develop.")))
        return TEXT("develop");

    if (Url.Contains(TEXT("://staging.")))
        return TEXT("staging");

    return TEXT("prod");
}

void DownloadFileAsync(
    const FString& Url,
    const FString& SavePath,
    TFunction<void(bool)> OnComplete)
{
    UE_LOG(LogBlackBox, Log, TEXT("Downloading: %s"), *Url);

    TSharedRef<IHttpRequest, ESPMode::ThreadSafe> Request = FHttpModule::Get().CreateRequest();

    Request->SetURL(Url);
    Request->SetVerb(TEXT("GET"));

    Request->OnProcessRequestComplete().BindLambda(
        [SavePath, OnComplete](FHttpRequestPtr Req,
                                FHttpResponsePtr Resp,
                                bool bSuccess)
        {
            if (!bSuccess || !Resp.IsValid())
            {
                UE_LOG(LogBlackBox, Error, TEXT("Download failed"));
                OnComplete(false);
                return;
            }

            if (!EHttpResponseCodes::IsOk(Resp->GetResponseCode()))
            {
                UE_LOG(LogBlackBox, Error, TEXT("Download HTTP error: %d"), Resp->GetResponseCode());
                OnComplete(false);
                return;
            }

            IPlatformFile& PF = FPlatformFileManager::Get().GetPlatformFile();

            PF.CreateDirectoryTree(*FPaths::GetPath(SavePath));

            if (!FFileHelper::SaveArrayToFile(
                    Resp->GetContent(), *SavePath))
            {
                UE_LOG(LogBlackBox, Error, TEXT("Failed to save helper file"));
                OnComplete(false);
                return;
            }

            UE_LOG(LogBlackBox, Log, TEXT("Downloaded successfully: %s"), *SavePath);

            OnComplete(true);
        });

    Request->ProcessRequest();
}
struct FBlackBoxDownloadState
{
    bool bCrashDone = false;
    bool bReporterDone = false;
    bool bSuccess = true;
    bool bAnyDownloaded = false;
};

void RunBlackBoxHelperInstaller(FOnBlackBoxInstallerFinished OnFinished)
{
    const FString BaseUrl = TEXT(__BLACKBOX_BASE_URL__);
    const FString Env = GetEnvFromBaseUrl(BaseUrl);
    if (Env != TEXT("prod"))
    {
        CrashHelperDownloadURL = CrashHelperDownloadURL.Replace(TEXT("prod"), *Env);
        IssueReporterDownloadURL = IssueReporterDownloadURL.Replace(TEXT("prod"), *Env);
    }

    TSharedRef<FBlackBoxDownloadState> State = MakeShared<FBlackBoxDownloadState>();

    auto TryFinish = [State, OnFinished]()
    {
        if (State->bCrashDone && State->bReporterDone)
        {
            OnFinished.ExecuteIfBound(State->bSuccess);

            // If any helper was freshly downloaded, it won't be active until the
            // editor is restarted — notify the user on the Game Thread so the
            // dialog is guaranteed to open on the main Slate thread.
            if (State->bAnyDownloaded && State->bSuccess)
            {
                AsyncTask(ENamedThreads::GameThread, []()
                {
                    const EAppReturnType::Type Result = FMessageDialog::Open(
                        EAppMsgType::YesNo,
                        FText::FromString(
                            TEXT("BlackBox SDK Helper files have been downloaded.\n\n")
                            TEXT("The editor must be restarted for the helpers to start working correctly.\n\n")
                            TEXT("Would you like to Restart Now?")),
                        FText::FromString(TEXT("BlackBox SDK — Restart Required")));

                    if (Result == EAppReturnType::Yes)
                    {
                        FUnrealEdMisc::Get().RestartEditor(false);
                    }
                });
            }
        }
    };

    IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();

    const FString CrashHelperPath = GetCrashHelperPath();
    if (!PlatformFile.FileExists(*CrashHelperPath))
    {
        DownloadFileAsync(
            CrashHelperDownloadURL,
            CrashHelperPath,
            [State, TryFinish](bool bOk)
            {
                State->bCrashDone = true;
                State->bSuccess &= bOk;
                if (bOk) State->bAnyDownloaded = true;
                TryFinish();
            });
    } else {
        State->bCrashDone = true;
        TryFinish();
    }

    const FString IssueReporterPath = GetIssueReporterPath();
    if (!PlatformFile.FileExists(*IssueReporterPath))
    {
        DownloadFileAsync(
            IssueReporterDownloadURL,
            IssueReporterPath,
            [State, TryFinish](bool bOk)
            {
                State->bReporterDone = true;
                State->bSuccess &= bOk;
                if (bOk) State->bAnyDownloaded = true;
                TryFinish();
            });
    } else {
        State->bReporterDone = true;
        TryFinish();
    }
}

#endif // WITH_EDITOR
