<?php

namespace App\Browser\Jobs\Hellcase;

use App\Browser\BrowserJob;
use App\Browser\Components\Hellcase\MainNav;
use App\Browser\JobDebugScreenshot;
use App\Browser\Jobs\Hellcase\HellcaseLoginQrCode;
use App\Models\JobArtifact;
use App\Models\JobRun;
use App\Notification\NotificationBody\Hellcase\HellcaseNotificationDailyFreeBody;
use App\Notification\NotificationBody\Hellcase\HellcaseNotificationLoginBody;
use App\Notification\Notifications\Hellcase\HellcaseNotificationDailyFree;
use App\Notification\Notifications\Hellcase\HellcaseNotificationLogin;
use App\Notification\Notifications\JobDebugNotification;
use App\Notification\Notifications\JobErrorNotification;
use App\Notification\Providers\AllNotification;
use Facebook\WebDriver\WebDriverBy;
use Illuminate\Contracts\Queue\ShouldBeUniqueUntilProcessing;
use Illuminate\Support\Facades\Log;
use Laravel\Dusk\Browser;

class HellcaseJob extends BrowserJob implements ShouldBeUniqueUntilProcessing
{
    private const STEAM_LOGIN_THRESHOLD = 5 * 60; // 5 minutes
    private const APPROXIMATIVE_RUNNING_MINUTES = 2;

    protected JobRun $jobRun;

    public function __construct($jobId = 2)
    {
        parent::__construct($jobId);
    }

    public function run(Browser $browser): ?JobRun
    {
        Log::info("Running HellcaseJob");
        $this->jobRun = new JobRun([
            "job_id" => $this->jobId,
            "success" => false,
        ]);
        $this->jobRun->save();

        $browser->visit('https://hellcase.com');
        $browser->waitForText("CASES", 30, true);
        $this->removePopups($browser);
        sleep(5);
        $this->signin($browser);
        try {
            $this->joinFreeGiveaways($browser);
        } catch (\Exception $e) {
            $this->jobRun->success = false;
            $this->jobRun->save();
            AllNotification::send(new JobErrorNotification($this->jobId, "Erreur lors de la participation aux concours gratuits : " . $e->getMessage()));
        }
        $this->getDailyFree($browser);

        $this->jobRun->success = true;
        $this->jobRun->save();

        Log::info("HellcaseJob run ended");

        return $this->jobRun;
    }

    /**
     * @inheritDoc
     */
    public function runTest(Browser $browser): ?JobRun
    {
        try {
            $browser->visit('https://hellcase.com');
            $browser->waitForText("CASES", 30, true);
            $this->removePopups($browser);
            sleep(2);
            $this->signin($browser);
            return $this->makeSimpleJobRun(
                true,
                "Connexion réussie",
                "Datboi a réussi à se connecter sur Hellcase"
            );
        } catch (\Exception $e) {
            return $this->makeSimpleJobRun(
                false,
                "Connexion échouée",
                "Datboi n'a pas réussi à se connecter sur Hellcase :\n" . $e->getMessage()
            );
        }
    }

    protected function signin(Browser $browser)
    {
        try {
            $browser->clickAtXPath('//button[.//span[text() = "Sign in"]]');
        } catch (\Exception $e) {
            return;
        }

        sleep(5);
        $browser->waitForText("Sign in with Steam", 30, true);
        sleep(3);
        $browser->driver->findElement(WebDriverBy::xpath("//button[. = 'Sign in with Steam']"))->click();
        sleep(5);

        // QR CODE SCANNING
        try {
            $browser->waitForTextIn("div", "Or sign in with QR", 30, true);
            sleep(10);
            try {
                $qrCode = $browser->driver->findElement(WebDriverBy::xpath('//div[./*[contains(text(), "Or sign in with QR")]]'));
            } catch (\Exception $e) {
                $browser->screenshot(JobDebugScreenshot::getFileName($this->jobId));
                AllNotification::send(new JobDebugNotification($this->jobId, "Le QR code de la page de connexion de Steam n'a pas été trouvé"));
                throw $e;
            }

            // Wait to be redirected to the Steam login page, while waiting take a new screenshot every 30 seconds
            $isBackOnHellcase = false;
            $secondsCounter = 0;
            while (!$isBackOnHellcase && $secondsCounter < self::STEAM_LOGIN_THRESHOLD) {
                // Take a screenshot of the QR code and send it
                $qrCode->takeElementScreenshot(HellcaseLoginQrCode::getImgFileAbsolutePath());
                AllNotification::send(
                    new HellcaseNotificationLogin(
                        $this->jobId,
                        new HellcaseNotificationLoginBody()
                    )
                );

                try {
                    $browser->waitForLocation("https://hellcase.com", HellcaseLoginQrCode::QR_CODE_VALIDITY); // The QR code is only valid for 20 seconds
                } catch (\Exception $e) {
                    $secondsCounter += HellcaseLoginQrCode::QR_CODE_VALIDITY; // we've waited for QR_CODE_VALIDITY seconds
                    continue;
                }
                $isBackOnHellcase = true;
            }
        } catch (\Exception $e) {
            // If the QR code is not found, we are not on the QR code page
            Log::debug("Exception because qrcode not found : " . $e);
            $isBackOnHellcase = true;
        } catch (\Throwable $e) {
            // If the QR code is not found, we are not on the QR code page
            Log::debug("Exception because qrcode not found : " . $e);
            $isBackOnHellcase = true;
        }

        if ($isBackOnHellcase) {
            // Click a button that says "sign in"
            $browser->waitForText("By signing into steam.loginhell.com through Steam", 30, true);
            $browser->clickAtXPath('//input[@id = "imageLogin"]');
            sleep(20);
        }
    }

    private function joinFreeGiveaways(Browser $browser)
    {
        $buttons = $browser->driver->findElements(WebDriverBy::xpath('//button[./div[text()="join"]]'));
        if (sizeof($buttons) == 0) {
            $browser->screenshot(JobDebugScreenshot::getFileName($this->jobId));
            AllNotification::send(new JobDebugNotification($this->jobId, "No join for free buttons found"));
            $this->jobRun->addArtifact(new JobArtifact([
                "name" => "Pas de concours joignable",
                "content" => ""
            ]));
            return;
        }

        try {
            // $nextSlideButton = $browser->driver->findElement(WebDriverBy::xpath('//button[@class="_button_1ygbm_7 _next_1ygbm_24"]'));
            $nextSlideButton = "Button next slide is deactivated";
        } catch (\Exception $e) {
            $browser->screenshot(JobDebugScreenshot::getFileName($this->jobId));
            AllNotification::send(new JobDebugNotification($this->jobId, "No next slide button found"));
            return;
        }
        foreach ($buttons as $button) {
            // Click the next slide button if the button is not clickable
            $clickedFailsCounter = 0;
            while ($clickedFailsCounter < 7 && $clickedFailsCounter >= 0) {
                try {
                    $button->click();
                } catch (\Exception $e) {
                    $clickedFailsCounter++;
                    try {
                        // $nextSlideButton->click();
                    } catch (\Exception $_) {}
                    sleep(3);
                    continue;
                }
                $clickedFailsCounter = -1;
            }

            sleep(5); // Wait a bit for loading
            // $this->joinGiveaway($browser);

            $browser->within(new MainNav, function (Browser $browser) {
                $browser->goToHome();
            });
        }
    }

    private function joinGiveaway(Browser $browser)
    {
        $joinButton = $browser->driver->findElement(WebDriverBy::xpath('//button[span[contains(text(), "Join for free")]]'));
        $joinButton->click();

        sleep(3);

        // JobRun
        // Get the elements text containing class starting with giveaway-entity-prize__
        $prizeElement = $browser->driver->findElements(WebDriverBy::xpath('//div[starts-with(@class, "giveaway-entity-prize__")]'));
        // Join their text
        $prize = "";
        foreach ($prizeElement as $element) {
            $prize .= $element->getText() . "\n";
        }
        $this->jobRun->addArtifact(new JobArtifact([
            "name" => "Rejoint le concours",
            "content" => "Pour tenter de gagner le prix : \n" . $prize
        ]));
    }

    private function getDailyFree(Browser $browser)
    {
        $browser->visit('https://hellcase.com/dailyfree');
        try {
            $browser->waitForText("Get Daily free loot", 30, true);
        } catch (\Exception $e) {
            return; // Pobably means we do not have access to a daily free loot
        }

        // Do we fill the conditions ?
        if (sizeof(value: $browser->driver->findElements(WebDriverBy::xpath('//p[contains(text(), "Fulfill the conditions below")]'))) > 0) {
            $this->fillDailyFreeConditions($browser);
        }

        // If we see "availible in 20 HR 49 MIN", parse the hours and minute and reschedule
        $availibleInButton = $browser->driver->findElement(WebDriverBy::xpath('//button[contains(@class, "daily-free-banner__button")]'));
        if ($availibleInButton->getAttribute("disabled") == "true") {
            $hours = $availibleInButton->getText();
            // If the text is like "in 26 sec." we need to put one minute
            if (str_contains(strtolower($hours), "seconds")) {
                $browser->screenshot(JobDebugScreenshot::getFileName($this->jobId));
                AllNotification::send(new JobDebugNotification($this->jobId, "I hate niggers"));
                // $this->reschedule(1);
                sleep(60);
                return $this->getDailyFree($browser);
            }
            $hours = explode(" ", $hours);
            $minutes = $hours[4];
            $hours = $hours[2];

            $this->jobRun->addArtifact(new JobArtifact([
                "name" => "Cadeau gratuit pas encore disponible",
                "content" => "Le cadeau gratuit journalier sera disponible dans {$hours} heures et {$minutes} minutes."
            ]));

            $this->reschedule($hours * 60 + $minutes + 1);

            return;
        }

        // Click the dailyfree button
        $availibleInButton->click();

        sleep(15);

        $browser->waitForText("Your drop", 30, true);

        $lootElement = $browser->driver->findElement(WebDriverBy::xpath('//div[contains(@class, "daily-free-win-bonus")]'));
        $lootElement->takeElementScreenshot(HellcaseDailyFreeScreenshot::getImgFileAbsolutePath());

        $this->reschedule((24 * 60) - ($this::APPROXIMATIVE_RUNNING_MINUTES / 2)); // Reschedule in 24hr -1min

        AllNotification::send(
            new HellcaseNotificationDailyFree($this->jobId, new HellcaseNotificationDailyFreeBody())
        );
    }

    /**
     * Must be on the dailyfree page
     * @param \Laravel\Dusk\Browser $browser
     * @throws \Exception
     * @return void
     */
    private function fillDailyFreeConditions(Browser $browser)
    {
        // 1. See what conditions we need to fullfill
        $conditions = [];
        $conditionsDivs = $browser->driver->findElements(WebDriverBy::xpath('//*[@class = "daily-free-requirement__heading-left"]'));
        for ($i = 0; $i < sizeof($conditionsDivs); $i++) {
            $conditionDiv = $conditionsDivs[$i];
            // See if the element has the completed class
            $conditions[$i] = [
                "isFilled" => str_contains($conditionDiv->getAttribute("class"), "completed"),
                "text" => $conditionDiv->getText()
            ];
        }

        if (sizeof($conditions) == 0) {
            throw new \Exception("No dailyfree conditions found");
        }

        if (!$conditions[0]["isFilled"]) {
            $this->changeSteamProfilePicture($browser);
        }
        if (!$conditions[1]["isFilled"]) {
            $this->changeSteamProfileToPublic($browser);
        }
    }

    /**
     *
     * Must be on the dailyfree page
     * @param \Laravel\Dusk\Browser $browser
     * @return void
     */
    private function changeSteamProfilePicture(Browser $browser)
    {
        // Get all of the availible image link
        $images = $browser->driver->findElements(WebDriverBy::xpath('//a[@class = "daily-free-user-requirement-avatar-item"]'));
        // Download the image from the second link in a special folder
        $imageLink = $images[1]->getAttribute("href");
        // Download the image in app/Browser/downloads/
        $imagePath = base_path("app/Browser/downloads/Hellcase/pp.png");
        file_put_contents($imagePath, file_get_contents($imageLink));

        $this->goToSteamProfileSettings($browser);

        // Wait for and click "Avatar"
        $this->waitForAndClickText($browser, "Avatar");

        // Wait for and click "Upload your avatar"
        $browser->waitForText("Upload your avatar", 30, true);
        // $browser->clickAtXPath('//*[contains(text(), "Upload your avatar")]');

        // Upload the downloaded image
        $browser->attach('input[type="file"]', $imagePath);

        // Wait for and click save
        $this->waitForAndClickText($browser, "Save");

        // Go back to dailyfree
        $browser->visit('https://hellcase.com/dailyfree');

        sleep(10);

        try {
            // wait and click "Check the condition"
            $this->waitForAndClickText($browser, "Check the condition");
            $browser->waitForText("Your Steam profile avatar does not match any of the ones specified in the", 30, true);
        } catch (\Exception $e) {
            // If the text is not found, the condition is filled
            return;
        }
    }

    private function changeSteamProfileToPublic(Browser $browser)
    {
        $this->goToSteamProfileSettings($browser);

        // Wait for and click "Privacy Settings"
        $this->waitForAndClickText($browser, "Privacy Settings");

        $dropdownButton = $browser->driver->findElement(WebDriverBy::xpath('//div[text() = "My profile"]/div'));
        $dropdownButton->click();

        sleep(2);

        // Div that contains the visible class ath the body root an element with public text
        $publicOption = $browser->driver->findElement(WebDriverBy::xpath('/body/div[contains(@class, "visible")]/div[contains(text(), "Public")]'));
        $publicOption->click();

        // Go back to dailyfree
        $browser->visit('https://hellcase.com/dailyfree');
    }

    /**
     * From the dailyfree page
     * @param \Laravel\Dusk\Browser $browser
     * @return void
     */
    private function goToSteamProfileSettings(Browser $browser)
    {
        // Get the link that has text "Steam profile"
        $steamProfileLink = $browser->driver->findElement(WebDriverBy::xpath('//a[contains(text(), "Steam profile")]'));
        $browser->visit($steamProfileLink->getAttribute("href"));
        $browser->waitForText("Level");

        // Click "Edit Profile
        $browser->clickAtXPath('//*[contains(text(), "Edit Profile")]');
    }

    protected function removePopups(Browser $browser)
    {
        // $browser->script('document.querySelector("div.app-modal")[0].remove();');
        // $browser->driver->executeScript('document.querySelector("div.app-modal")[0].remove();');
    }
}
