<?php
namespace LaraBB\Tracker;

use LaraBB\Profile\Models\Profile;
use PerfectPurple\Models\TorrentPeer;
use PerfectPurple\Models\TorrentFile;
use stdClass;

//use PerfectPurple\Models\TorrentHistory;

class Announce {

    /**
     * @var
     */
    public  $config;

    /**
     * @var stdClass
     */
    public  $request;

    private string $browsers = 'Mozilla|Chrome|Opera|Lynx|Trident|Edge';

    /**
     * @var
     */
    private $currentPeer;

    /**
     * @var
     */
    private $currentTorrent;

    /**
     * @var UserProfile
     */
    private $userProfile;

    /**
     * @var TorrentPeer
     */
    private $torrentPeer;

    /**
     * @var TorrentFile
     */
    private $torrentFile;
    //private $torrentHistory;

    /**
     * TorrentTracker constructor.
     * @param UserProfile $userProfile
     * @param TorrentPeer $torrentPeer
     * @param TorrentFile $torrentFile
     */
    public function __construct(
        UserProfile $userProfile,
        TorrentPeer $torrentPeer,
        TorrentFile $torrentFile)
    {
        $this->config         = config('perfectpurple.tracker');
        $this->userProfile    = $userProfile;
        $this->torrentPeer    = $torrentPeer;
        $this->torrentFile    = $torrentFile;
        //$this->torrentHistory = $torrentHistory;
        $this->request = new stdClass();
    }

    /**
     * @return bool
     */
    public function checkAccess(): bool
    {
        return !preg_match('/' . $this->browsers . '/', $this->request->agent);
    }

    /**
     * @return bool
     */
    public function checkClientValues(): bool
    {
        return !empty($this->request->port);
    }

    /**
     * @return bool
     */
    public function checkTorrent(): bool
    {
        if(empty($this->request->infoHash)) {
            return false;
        }

        $this->currentTorrent = $this->torrentFile->readByInfoHash($this->request->infoHash);

        return !is_null($this->currentTorrent);
    }

    /**
     * @return bool
     */
    public function checkPid(): bool
    {
        $userProfile = $this->userProfile->readByPid($this->request->pid);

        return !is_null($userProfile);
    }

    /**
     * @return bool
     */
    public function checkSamePid(): bool
    {
        $data = $this->torrentPeer->status($this->request->pid, $this->currentTorrent->id);
        if(!$data->isEmpty()) {
            foreach($data as $set) {
                if($set->status == 'seeder' && $set->status_cnt >= $this->config['max_pid_seed']) return false;
                if($set->status == 'leecher' && $set->status_cnt >= $this->config['max_pid_leech']) return false;
            }

            return true;
        }

        return true;
    }

    /**
     * @return $this
     */
    public function checkFirewalled(): Announce
    {
        $theError = '';
        $fd       = fsockopen($this->request->ip, $this->request->port, $errno, $theError, 10);

        $this->request->nat = !$fd;
        fclose($fd);

        return $this;
    }

    /**
     * @return bool|string
     */
    public function checkClientEvent(): bool|string
    {
        $this->currentPeer = $this->torrentPeer->read($this->request->pid, $this->currentTorrent->uuid, ['profile']);

        return match ($this->request->event) {
            'started' => $this->torrentStarted(),
            'stopped'   => $this->torrentStopped(),
            'finished' => $this->torrentFinished(),
            empty($this->currentRequest->event) => $this->torrentNoEvent(),
            default => false,
        };

    }

    /**
     * @return string
     */
    private function torrentStarted(): string
    {
        if(!is_null($this->currentPeer)) {
            $this->torrentPeer->edit($this->currentPeer, $this->request);
        } else {
            $this->torrentPeer->add($this->request, $this->currentTorrent);
            $this->currentPeer = $this->torrentPeer;
        }

        /*$history = $this->torrentHistory->read($this->currentPeer->user->uid, $this->currentTorrent->id);
        if(!is_null($history)) {
            $this->torrentHistory->edit($history, $this->currentRequest, true);
        } elseif(is_null($history) && $this->currentRequest->left > 0) {
            $this->torrentHistory->add($this->currentPeer, $this->currentTorrent, $this->currentRequest, true);
        }*/

        $this->torrentFile->setSeedersLeechers($this->currentTorrent, $this->request);
        return $this->getRandomPeers();
    }

    /**
     * @return string
     */
    private function torrentStopped(): string
    {
        $this->torrentPeer->kill($this->currentPeer, $this->currentTorrent);
        $this->userProfile->setTraffic($this->currentPeer->profile, $this->request);

        /*$history = $this->torrentHistory->read($this->currentPeer->user->uid, $this->currentTorrent->id);
        if(!is_null($history)) {
            $this->torrentHistory->edit($history, $this->currentRequest, false);
        }*/

        return $this->getRandomPeers();
    }

    /**
     * @return string
     */
    private function torrentFinished(): string
    {
        if(is_null($this->currentPeer)) {
            $this->torrentPeer->add($this->request, $this->currentTorrent);
            $this->currentPeer = $this->torrentPeer;
        }

        $this->torrentPeer->complete($this->currentPeer, $this->request);
        $this->torrentFile->complete($this->currentTorrent);

        /*$history = $this->torrentHistory->read($this->currentPeer->user->uid, $this->currentTorrent->id);
        if(!is_null($history)) {
            $this->torrentHistory->edit($history, $this->currentRequest, true, true);
        } else {
            $this->torrentHistory->add($this->currentPeer, $this->currentTorrent, $this->currentRequest, true, true);
        }*/

        return $this->getRandomPeers();
    }

    /**
     * @return string
     */
    private function torrentNoEvent(): string
    {
        if(is_null($this->currentPeer)) {
            $this->torrentPeer->add($this->request, $this->currentTorrent);
            $this->currentPeer = $this->torrentPeer;
        }

        if($this->currentPeer->bytes != 0 && $this->request->left == 0) {
            $this->torrentPeer->complete($this->currentPeer, $this->request);
            $this->torrentFile->complete($this->currentTorrent);
        }

        $this->collectBytes();

        return $this->getRandomPeers();
    }

    /**
     * @return void
     */
    private function collectBytes(): void
    {
        $diff = bcsub($this->currentPeer->bytes, $this->currentPeer->left);

        $this->torrentPeer->setBytes($this->currentPeer, $this->request, $diff);
    }

    /**
     * @return string
     */
    private function getRandomPeers(): string
    {
        $data = $this->torrentPeer->random($this->currentTorrent->uuid, $this->request->numWant);
        if(!is_null($data) && $this->request->compact) {
            $compactStr = '';
            $data->map(function($set) use(&$compactStr) {
                $compactStr .= str_pad(pack('Nn', ip2long($set->ip), $set->port), 6);
            });

            return $compactStr;
        }

        return $data;
    }
}
