// This program wasn't to have comments but it's late and I'M BORED so here goes !!!

using Radium.DataExporter.Client.Networking;
using Radium.DataExporter.Client.Platforms;
using Radium.DataExporter.Models.Accounts;
using Serilog;
using Serilog.Events;
using Spectre.Console;
using System.Text.Json;

namespace Radium.DataExporter.Client;

internal class Program
{
    private static async Task Main(string[] args)
    {
        // Let's store the title so we can add cool stuff appended to it later!
        var title = $"Radium Data Export Client - v{Constants.Version} ";

        // ...and set it
        Console.Title = title;

        // Setup Serilog
        Log.Logger = new LoggerConfiguration()
            .MinimumLevel.Debug()
            .WriteTo.Console()

            .WriteTo.File(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log", "log-.txt"),
            rollingInterval: RollingInterval.Day,
            retainedFileCountLimit: 5)

            .WriteTo.File(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log", "error-.txt"),
            rollingInterval: RollingInterval.Day,
            restrictedToMinimumLevel: LogEventLevel.Error)

            .Enrich.FromLogContext()
            .CreateLogger();

        ILogger logger = Log.Logger;

        // Show cool Radium logo
        PrintLogo();

        Console.WriteLine();
        logger.Information($"Radium Data Export Client - v{Constants.Version} made with half love & half hatred by @splootybean");

        Thread.Sleep(3000);

        // Create instance of RadiumHttpClient so we can make requests later on
        var httpClient = new RadiumHttpClient();

        // along with a platform manager! I wish this wasn't hardcoded but honestly I doubt you'd be on anyyyyy other platform :p
        var platformManager = new SteamPlatformManager();
#if DEBUG
        logger.Debug($"Initalizing PlatformManager for {platformManager.Platform}");
#endif
        // initialize our platform manager
        platformManager.Initialize();

#if DEBUG
        logger.Debug($"GetUsableCachedLoginsAsync for {platformManager.PlatformId}");
#endif
        // get all the cached logins for this platform so we can display them in the account selector
        var cachedLogins = await httpClient.GetUsableCachedLoginsAsync(
            platformManager.Platform,
            platformManager.PlatformId)!;

        // we treat this as it being null (or maybe you really just dont play rec room!)
        if (cachedLogins.Count <= 0)
        {
            logger.Information("No Rec Room accounts!");
            return;
        }

        logger.Information($"Found {cachedLogins.Count} usable cached logins");

        // get accounts
        var accounts = await httpClient.GetAccountsBulkAsync([.. cachedLogins
            .Select(x => x.AccountId)]);
#if DEBUG
        foreach (var account in accounts)
            logger.Debug(account.Username);
#endif
        Console.Clear();

        // this is the cool appending thing i was talking about
        Console.Title = title + "(Account Selection)";

        // prompt to select an account!
        var selection = AnsiConsole.Prompt(new SelectionPrompt<Account>()
            .Title("[blue]Select an account to export data for[/]")
            .PageSize(12)
            .UseConverter(x => Markup.Escape($"[{x.AccountId}] ({x.Username}) {x.DisplayName}"))
            .AddChoices(accounts));

        logger.Information($"Account selected: {selection.Username}");

        // JOB CREATION

        logger.Information($"Okay {selection.Username}! You are now going to generate an authentication code to link your RecNet account to your Radium account.");
        logger.Information($"Go to {Constants.ProdUri} to generate an authentication code, and type it in the box below. When you're done, press ENTER.");

        Console.Title = title + "(Account Linking)";

        
        // get our moving-out code
        var code = AnsiConsole.Prompt(
            new TextPrompt<string>("Please enter your 6-digit code:")
            .Validate(code =>
            {
                return code.Length == 6 && code.All(char.IsDigit)
                ? ValidationResult.Success()
                : ValidationResult.Error("[red]Code must be 6-digits, and numeric![/]");
            }));

        Console.Clear();

        logger.Information("Creating session...");
        
        // get auth params to send to login
        var authParams = platformManager.GetPlatformAuthenticationParamaters();
        logger.Information($"Get {platformManager.Platform} AuthenticationParamaters OK!");

        // create a job!
        var response = await httpClient.CreateJobSessionAsync(code, selection.AccountId, platformManager.PlatformId, authParams);

        // check if it failed or not...
        if (!response.IsSuccess)
        {
            Console.Title = title + $"({response.Error?.Title})";
            // lets print the full error if we're in debug, else we can show the bare minimum
#if DEBUG
            logger.Fatal(JsonSerializer.Serialize(response.Error, Models.JsonContext.RecNetJsonContext.Default.RecNetError));
#else
            logger.Fatal($"Could not get job from Radeon! {response.Error?.Detail} ({response.Error?.Title})");
#endif
            Console.ReadKey();
            // funny rec room number
            Environment.Exit(87);
        }

        var job = response.Result!;

        Console.Title = title;

        Console.Clear();

        logger.Information($"{(job.ResumedExistingJob ? "Resuming" : "Started")} job for {selection.Username} (ID: {job.JobId})!");

        logger.Information("You may now close the application and go back to the website.");

        Thread.Sleep(10000);
    }

    public static void PrintLogo()
    {
        Console.WriteLine(" _______    ______   _______   ______  __    __  __       __ ");
        Console.WriteLine("/       \\  /      \\ /       \\ /      |/  |  /  |/  \\     /  |");
        Console.WriteLine("$$$$$$$  |/$$$$$$  |$$$$$$$  |$$$$$$/ $$ |  $$ |$$  \\   /$$ |");
        Console.WriteLine("$$ |__$$ |$$ |__$$ |$$ |  $$ |  $$ |  $$ |  $$ |$$$  \\ /$$$ |");
        Console.WriteLine("$$    $$< $$    $$ |$$ |  $$ |  $$ |  $$ |  $$ |$$$$  /$$$$ |");
        Console.WriteLine("$$$$$$$  |$$$$$$$$ |$$ |  $$ |  $$ |  $$ |  $$ |$$ $$ $$/$$ |");
        Console.WriteLine("$$ |  $$ |$$ |  $$ |$$ |__$$ | _$$ |_ $$ \\__$$ |$$ |$$$/ $$ |");
        Console.WriteLine("$$ |  $$ |$$ |  $$ |$$    $$/ / $$   |$$    $$/ $$ | $/  $$ |");
        Console.WriteLine("$$/   $$/ $$/   $$/ $$$$$$$/  $$$$$$/  $$$$$$/  $$/      $$/ ");
    }
}