<?php

namespace App\Browser\Jobs\InstagramRepost;

use App\Browser\JobDebugScreenshot;
use App\Browser\Jobs\Instagram\InstagramAbstractJob;
use App\Browser\Jobs\InstagramRepost\DescriptionPipeline\InstagramDescriptionPipeline;
use App\Models\InstagramNotification;
use App\Models\InstagramRepost;
use App\Models\Job;
use App\Models\JobRun;
use App\Notification\Notifications\JobDebugNotification;
use App\Notification\Providers\AllNotification;
use App\Services\Instagram\NotificationTypeDetector;
use Facebook\WebDriver\WebDriverBy;
use Illuminate\Contracts\Queue\ShouldBeUniqueUntilProcessing;
use Illuminate\Support\Facades\Log;
use Laravel\Dusk\Browser;
use App\Services\AIPrompt\OpenAPIPrompt;

class InstagramNotificationHandlingJob extends InstagramAbstractJob implements ShouldBeUniqueUntilProcessing
{
    // === CONFIGURATION ===

    public $timeout = 1800; // 30 minutes

    private const APPROXIMATIVE_RUNNING_MINUTES = 2;

    protected IInstagramVideoDownloader $videoDownloader;

    protected ReelDescriptor $ReelDescriptor;

    protected string $downloadFolder = "app/Browser/downloads/InstagramRepost/";

    /**
     * Pipeline for processing Instagram post descriptions.
     * This pipeline can be used to modify the description before reposting.
     * For example, it can remove account references or add hashtags.
     *
     * @var InstagramDescriptionPipeline
     */
    protected InstagramDescriptionPipeline $descriptionPipeline;

    public function __construct($jobId = 4, ReelDescriptor $ReelDescriptor = null, OpenAPIPrompt $openAPIPrompt = null)
    {
        parent::__construct($jobId, $openAPIPrompt);

        $this->downloadFolder = base_path($this->downloadFolder);
        $this->videoDownloader = new YTDLPDownloader();
        $this->ReelDescriptor = $ReelDescriptor ?? app(ReelDescriptor::class);
        $this->descriptionPipeline = new InstagramDescriptionPipeline([
            // Add steps to the pipeline here
            new DescriptionPipeline\RemoveAccountsReferenceStep(),
        ]);
    }

    public function run(Browser $browser): ?JobRun
    {
        $startTime = microtime(true);

        Log::info("Running InstagramNotificationHandlingJob");
        $this->jobInfos = Job::find($this->jobId)->jobInfosTable();
        $this->jobRun = new JobRun([
            "job_id" => $this->jobId,
            "success" => false,
        ]);
        $this->jobRun->save();

        dump("visiting " . microtime(true) - $startTime);
        $browser->visit('https://instagram.com');
        sleep(5);
        // dump("removing popups " . microtime(true) - $startTime);
        // $this->removePopups($browser); // TEMPORARY DISABLED
        dump("signing in " . microtime(true) - $startTime);
        $this->signin($browser);
        sleep(2);
        dump("Saving notifications " . microtime(true) - $startTime);
        $this->saveNotifications($browser);
        sleep(seconds: 5);

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

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

        return $this->jobRun;
    }

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

    private function saveNotifications(Browser $browser): ?array
    {
        $this->openNotificationsPanel($browser);
        try {
            $DOMnotifications = $browser->driver->findElements(WebDriverBy::xpath('//div[@class="x6s0dn4 x1q4h3jn x78zum5 x1y1aw1k x64bnmy xwib8y2 x13jy36j x87ps6o x1wq6e7o x1ffbijf x1h4gsww xnp2e5m x1ypdohk x1l895ks"]')); // TODO : not rely on class names
            $notifications = [];
            foreach ($DOMnotifications as $DOMnotification) {
                // Process each notification
                $DOMnotificationText = $DOMnotification->findElement(WebDriverBy::xpath('./div[2]/span'));

                $notification = new InstagramNotification();
                $notification->username = str_replace("/", "", $DOMnotificationText->findElement(WebDriverBy::xpath('./a[1]'))->getAttribute('href'));
                $notification->notification_type = NotificationTypeDetector::detectType($DOMnotificationText->getText());
                // 05/08/2025 : Instagram removed the link to the post in the notification, the day I'm working on it :()
                $postId = null;
                try {
                    $postId = str_replace(["\/p/", "/"], "", $DOMnotification->findElement(WebDriverBy::xpath('./div[3]/a'))->getAttribute('href'));
                    dump("Post ID: " . $postId);
                    $repostId = InstagramRepost::where('repost_reel_id', $postId)->first()?->id;
                    dump("Repost ID: " . $repostId);
                    if ($repostId) {
                        $notification->instagram_repost_id = $repostId;
                    }
                } catch (\Exception $e) {
                    Log::error("Failed to get post ID from notification : " . $e->getMessage());
                }
                $notification->message = $DOMnotificationText->getText();

                // Save only if the notification is not already saved
                try {
                    $notification->save();
                } catch (\Exception $e) {
                    Log::error("Failed to save notification : " . $e->getMessage());
                    break; // Stop processing further notifications if one fails
                }
                dump("Notification saved : " . $notification);
                $notifications[] = $notification;
            }
            return $notifications;
        } catch (\Exception $e) {
            Log::error("Failed to get notifications : " . $e->getMessage());
            $browser->screenshot(JobDebugScreenshot::getFileName($this->jobId));
            AllNotification::send(new JobDebugNotification($this->jobId, "Failed to open notifications panel: " . $e->getMessage()));
            return null;
        }
    }

    private function openNotificationsPanel(Browser $browser): void
    {
        try {
            $notificationsButtonXpath = '//a[./div//span[contains(text(), "Notifications")]]';
            $notificationsButton = $browser->driver->findElement(WebDriverBy::xpath($notificationsButtonXpath));
            // $notificationsButton->click();
            $this->clickElementWithJavaScript($browser, $notificationsButtonXpath);
            sleep(1);
            $browser->waitForText('Filter', 10, true);
        } catch (\Exception $e) {
            Log::error("Failed to open notifications panel : " . $e->getMessage());
            $browser->screenshot(JobDebugScreenshot::getFileName($this->jobId));
            AllNotification::send(new JobDebugNotification($this->jobId, "Failed to open notifications panel: " . $e->getMessage()));
            return;
        }

        if (app()->environment('local')) {
            Log::debug("Notifications panel opened successfully waiting 7 seconds for manual interaction");
            sleep(7); // Allow time for manual interaction in local environment
        }
    }
}
