<?php
require_once 'vendor/autoload.php';

use PHPHtmlParser\Dom;
use PHPHtmlParser\Options;
use SlackPhp\BlockKit\Blocks\Context;
use SlackPhp\BlockKit\Blocks\Divider;
use SlackPhp\BlockKit\Blocks\Section;
use SlackPhp\BlockKit\Kit;
use SlackPhp\BlockKit\Partials\MrkdwnText;
use SlackPhp\BlockKit\Surfaces\Message;

ini_set('max_execution_time', -1);

class SiteScan
{
    private $site;
    private $request;
    private $gos;
    private $method;
    private $list;

    public function __construct()
    {
        $this->method = get_option("alfchecker__alc_method");
        $this->list = get_option("alfchecker__alc_list");

        $this->gos = explode(",", get_option("alfchecker__alc_value"));

        $this->site = (object)
                            [
                                'url' => get_site_url(),
                                'hook' => get_option("alfchecker__alc_slack_hook"),
                                'email' => get_option("alfchecker__alc_email")
                            ];
    }

    public function handle()
    {
        $start = date('Y-m-d H:i:s');
        $statuses = [];

        if ($this->aflc_isValid($this->site->url)) {
            
            $parser = $this->aflc_parse($this->site->url);

            $statuses[$this->site->url] = ['code' => $parser['code'], 'links' => []];

            if ($parser['code'] == 200) {
                foreach ($this->aflc_getLinks($parser['response']) as $link) {
                    $parser = $this->aflc_parse($link);

                    $statuses[$this->site->url]['links'][$link] = [
                        'code' => $parser['code'],
                        'links' => []
                    ];

                    if ($parser['code'] == 200) {
                        $statuses[$this->site->url]['links'][$link]['links'] = $this->aflc_getLinks($parser['response'], false);
                    }

                    usleep(500);
                }
            }
        }
        

        $missing = [];
        $ctas = [];

        foreach ($statuses as $url => $url_links) {
            $missing[$url] = [];
            if ($url_links['code'] != 200) {
                $missing[$url]['code'] = $url_links['code'];
                $missing[$url]['status'] = 'nok';
            }

            if (!$this->aflc_handleURL($url) && parse_url($this->site->url)['host'] == parse_url($url)['host'] && !array_key_exists($url, $ctas) && $this->aflc_isCtaLink($url)) {
                $ctas[$url] = false;
            }

            foreach ($url_links['links'] as $suburl => $sublinks) {
                if (!in_array($sublinks['code'], [200, 203])) {
                    $missing[$url][$suburl]['code'] = $sublinks['code'];
                }

                if (!$this->aflc_handleURL($suburl) && parse_url($this->site->url)['host'] == parse_url($suburl)['host'] && !array_key_exists($suburl, $ctas) && $this->aflc_isCtaLink($suburl)) {
                    $ctas[$suburl] = false;
                }

                if ($this->aflc_handleURL($suburl) || (!in_array($sublinks['code'], [200, 203]))) {
                    $missing[$url][$suburl]['status'] = 'nok';
                }


                foreach ($sublinks['links'] as $link) {
                    if (!$this->aflc_handleURL($link) && parse_url($this->site->url)['host'] == parse_url($link)['host'] && !array_key_exists($link, $ctas) && $this->aflc_isCtaLink($link)) {
                        $ctas[$link] = false;
                    }

                    if ($this->aflc_handleURL($link)) {
                        if (!isset($missing[$url][$suburl]['links'])) {
                            $missing[$url][$suburl]['links'] = [];
                        }

                        if (!isset($missing[$url][$suburl]['links'][$link])) {
                            $missing[$url][$suburl]['links'][$link] = 0;
                        }

                        $missing[$url][$suburl]['links'][$link]++;
                    }
                }
            }
        }


        foreach ($ctas as $url => &$cta) {
            $parser = $this->aflc_parse($url);

            if (strpos($parser['url'], $url) !== false) {
                $cta = true;
            }
        }

        $emailBody = "";

        $end = date('Y-m-d H:i:s');

        $headerSlack = ':clock1: *Start:* ' . $start.' *End:* ' . $end . ' | :page_facing_up: Report for: ' . $this->site->url;
        $headerEmail = '&#128336; <strong>Start:</strong> ' . $start.' <strong>End:</strong> ' . $end . ' | &#128195; <strong>Report for:</strong> ' . $this->site->url;

        $msg = Kit::newMessage()
            ->add( new Context(null, [
                new MrkdwnText($headerSlack)
            ]));

        $emailBody .= $headerEmail."<br/><br/>";

        $i = 0;

        foreach ($missing as $url => $pages) {
            foreach ($pages as $link => $page) {
                if (isset($page['links'])) {
                    foreach ($page['links'] as $ctalink => $num) {
                        $slackText = ':dart: CTA slug missing detected at <'.$link.'> ' . $ctalink . ' => ' . $num . ' time' . ($num != 1 ? 's' : '');
                        $emailText = '&#128721; <strong>CTA slug missing</strong> detected at <'.$link.'> ' . $ctalink . ' => ' . $num . ' time' . ($num != 1 ? 's' : '') . '<br>';
                        $msg->add(new Divider());
                        $msg->add(new Section(null, $brokenLink ));
                        $emailBody .= $emailText;
                        $i++;
                    }
                }
            }
        }

        if ($i == 0) {
            $slackText = ':white_check_mark: No broken affiliate links were detected. Thank you. (Check the links yourselves on important pages occasionaly, I can fail.)';
            $emailText = '&#128154; No broken affiliate links were detected. Thank you. <br><br><strong>Check the links yourselves on important pages occasionaly, I can fail.</strong><br><br>';
            $msg->add(new Divider());
            $msg->add(new Section(null, $slackText));
            $emailBody .= $emailText;
        }

        $i = 0;

        foreach ($missing as $url => $pages) {
            foreach ($pages as $link => $page) {
                // error codes != 200
                if (isset($page['status']) && $page['status'] == 'nok') {
                    $slackText = ':warning: CTA error code detected at <'.$link.'> (error code: ' . $page['code'] . ')';
                    $emailText = '&#128310; CTA error code detected at ('.$link.') (error code: ' . $page['code'] . ')<br>';
                    if ($i == 0) {
                        $msg->add(new Divider());
                    }
                    $i++;
                    $msg->add( new Context(null, [
                        new MrkdwnText($slackText)
                    ]));
                    $emailBody .= $emailText;
                }
            }
        }

        $i = 0;

        foreach ($ctas as $url => $cta) {
            if ($cta) {
                if ($i == 0) {
                    $msg->add(new Divider());
                    $i++;
                }
                $slackText = ':arrows_counterclockwise: CTA doesn\'t follow to anywhere. It stays at the same page. ('.$url.')';
                $emailText = '&#128257; CTA doesn\'t follow to anywhere. It stays at the same page. ('.$url.')<br>';
                $msg->add( new Context(null, [
                    new MrkdwnText($slackText)
                ]));
                $emailBody .= $emailText;
            }
        }


        if ($this->site->hook) $this->aflc_slack($msg);
        if ($this->site->email) $this->aflc_sendEmail($emailBody);
    }

    private function aflc_handleURL($url){

        if($this->method == "contain") {
            return in_array(substr($url, -4), $this->gos);
        } else if ($this->method == "list") {
            return in_array($url, explode(",", $this->list));
        } else {
            return ($url == $this->gos[0]) ? true : false;
        }

    }

    private function aflc_parse($url) {
        $args = array(
            'redirection' => '10',
        );

        $response = wp_remote_get( esc_url_raw($url), $args );
      
        if(is_wp_error($response)){
            return [
                'code' =>  200, 
                'response' => '', 
                'url' => $url
            ];
        } else {
            return [
                'code' =>  wp_remote_retrieve_response_code( $response ), 
                'response' => $response['body'], 
                'url' => $response['headers']['location']
            ];
        }
        
    }

    private function aflc_isValid($url) {
        if (filter_var($url, FILTER_VALIDATE_URL) && parse_url($url)['scheme'] != 'mailto') {
            return true;
        }
        return false;
    }

    private function aflc_getLinks($response, $unique = true) {
        if (empty($response)) {
            return [];
        }

        ini_set("mbstring.regex_retry_limit", "10000000");

        $dom = new Dom();
        $dom->loadStr($response);

        $links = [];

        foreach ($dom->getElementsbyTag('a') as $element) {
            if ($this->aflc_isValid($element->href)) {
                $links []= substr($element->href, -1) == '/' ? substr($element->href, 0, -1) : $element->href;
            }
        }

        if (!$unique) {
            return array_values($links);
        }

        return array_values(array_unique($links));
    }

    private function aflc_sendEmail($emailBody) {
        $email = get_option("admin_email");
        $to = $this->site->email;
        $subject = "Gambling Affiliate Link Checker - Report ".date('Y-m-d H:i');
        $headers = 'From: '. $email . "\r\n" .
            'Reply-To: ' . $email . "\r\n" .
            'Content-Type: text/html; charset=UTF-8';

        $sent = mail($to, $subject, $emailBody, $headers);
        return ($sent) ? true : false;
    }

    private function  aflc_slack( $data) {
        $args = array(
            'body'        => json_encode($data),
            'timeout'     => '10',
            'redirection' => '5',
            'httpversion' => '1.0',
            'blocking'    => true,
            'headers'     => array('Content-Type: application/json'),
            'cookies'     => array(),
        );
        $response = wp_remote_post( $this->site->hook, $args );
        
        return $response;
    }

    private function  aflc_isCtaLink($link) {
        $found = false;

        foreach ($this->gos as $go) {
            if (strpos($link, $go) !== false) {
                $found = true;
            }
        }

        return $found;
    }
}