<?php
// Silence may be golden, but sometimes words paint the canvas of understanding.

namespace Plugli\Linksy\SearchAndReplace\Admin\Partials\SearchAndReplace;

use Linksy\SearchAndReplace\Plugli\Standard\Helpers\Database\Database;

trait Utils
{
    private function replace_anchors($post, $anchors)
    {
        $stats = ['failed' => [], 'replaced' => []];

        if (!$post->get_editor() || empty($anchors)) {
            return $stats;
        }

        foreach ($anchors as $anchor) {
            $link = $anchor['view_url'];
            $phrase = $anchor['anchor'];

            $post_content = $post->get_content();
            $pattern = '/<a\\b([^>]*href="' . preg_quote($link, '/') . '"[^>]*)>' . preg_quote($phrase, '/') . '<\\/a>/i';

            if (preg_match($pattern, $post_content, $matches)) {
                $replacement = '<a' . $matches[1] . '>' . $anchor['anchor_replacement'] . '</a>';
            }

            if ($replacement !== null && !empty($matches[0])) {
                $status_data = $post->save_post($post->get_title(), $matches[0], $matches[0], $replacement);
                $success = $status_data[0];
            } else {
                $success = false;
            }

            if (!empty($stats)) {
                $success ? $stats['replaced'][] = $phrase : $stats['failed'][] = $phrase;
            }
        }
        
        return $stats;
    }

    private function fix_anchors($post, array $anchors)
    {
        $stats = ['failed' => [], 'fixed' => []];
        $replacement = null;

        if (!$post->get_editor() || empty($anchors)) {
            return $stats;
        }

        foreach ($anchors as $anchor) {
            $anchor = (object) $anchor;
            $link = $anchor->view_url;
            $phrase = $anchor->anchor;

            $post_content = $post->get_content();
            $pattern = '/<a\\b([^>]*href="' . preg_quote($link, '/') . '"[^>]*)>' . preg_quote($phrase, '/') . '<\\/a>/i';

            if (preg_match($pattern, $post_content, $matches)) {
                $starting_tag = '<a' . $matches[1] . '>';
                $space_pattern = '/[\x{00A0}]/u';

				if (preg_match($space_pattern, $phrase, $space_match)) {
					$phrase = str_replace("\xC2\xA0", ' ', $phrase);
				}
            
                switch (true) {
                    case substr($phrase, -1) === ',':
                        $replacement = $starting_tag . rtrim($phrase, ',') . '</a>,';
                        break;
                    case strpos($phrase, ', ') === 0:
                        $replacement = ', ' . $starting_tag . ltrim($phrase, ', ') . '</a>';
                        break;
                    case substr($phrase, -1) === ' ':
                        $replacement = $starting_tag . rtrim($phrase, ' ') . '</a> ';
                        break;
                    case strpos($phrase, ' ') === 0:
                        $replacement = ' ' . $starting_tag . ltrim($phrase, ' ') . '</a>';
                        break;
                    case substr($phrase, -1) === '.':
                        $replacement = $starting_tag . rtrim($phrase, '.') . '</a>.';
                        break;
                    case strpos($phrase, '. ') === 0:
                        $replacement = '. ' . $starting_tag . ltrim($phrase, '. ') . '</a>';
                        break;
                    default:
                        $replacement = null;
                        break;
                }
            }

            if ($replacement !== null && !empty($matches[0])) {
                $status_data = $post->save_post($post->get_title(), $matches[0], $matches[0], $replacement);
                $success = $status_data[0];
            } else {
                $success = false;
            }
            
            if (!empty($stats)) {
                $success ? $stats['fixed'][] = $phrase : $stats['failed'][] = $phrase;
            }
        }
        
        return $stats;
    }

    private function linksy_broken_link_check( $link, $LINK_CONNECTION_TIMEOUT = 12, $is_clean_url = false) {
        if (!$link) {
            return null;
        }

        $url = $is_clean_url ? $link->clean_url : $link->url;

        $args = array(
            'timeout'    => $LINK_CONNECTION_TIMEOUT,
            'user-agent' => 'WordPress/' . get_bloginfo( 'version' ) . '; ' . get_bloginfo( 'url' )
        );

        $response = wp_remote_get( $url, $args );

        if ( is_wp_error( $response ) ) {
            $error_code    = $response->get_error_code();
            $error_message = htmlentities( $response->get_error_message() ); // Prevent XSS
            $log = sprintf("Error code : %s\nError message : %s\n", $error_code, $error_message);

            $http_code   = null;
            $is_timeout  = false;
            $final_url   = $url;
            $status_text = $error_code;

            if ( $error_code === 'http_request_failed' ) {
                if ( strpos( $error_message, 'timed out' ) !== false ) {
                    $is_timeout = true;
                } elseif ( strpos( $error_message, "Couldn't resolve host" ) !== false ) {
                    $http_code = 521;
                } elseif ( strpos( $error_message, "Couldn't connect to server" ) !== false ) {
                    $http_code = 522;
                }else {
                    $http_code = 500;
                }
            } else {
                $http_code = 500;
            }

            $result_hash = md5( $error_code . $error_message );

            return [
                'post_id'       => $link->post_id,
                'anchor'        => $link->anchor,
                'parser_type'   => 'link',
                'url'           => $url,
                'last_checked'  => current_time('mysql'),
                'check_count'   => 1,
                'final_url'     => $final_url,
                'log'           => $log,
                'http_code'     => $http_code,
                'status_code'   => $http_code,
                'status_text'   => $status_text,
                'response_time' => '',
                'result_hash'   => $result_hash,
                'may_recheck'   => true,
                'is_timeout'    => $is_timeout,
                'is_broken'     => true,
                'is_warning'    => false,
                'is_dismissed'  => 0,
            ];
        }

        $result = $this->process_wp_http_response($response, $link, $is_clean_url);

        return $result;
    }

    private function linksy_multi_broken_links_check($links, $LINK_CONNECTION_TIMEOUT = 12, $is_clean_url = false) {
        if (empty($links)) {
            return [];
        }
    
        $results = [];
    
        foreach ($links as $link) {
            $url_key = $is_clean_url ? $link->clean_url : $link->url;
    
            $link_result = $this->linksy_broken_link_check($link, $LINK_CONNECTION_TIMEOUT, $is_clean_url);
    
            $results[$url_key] = $link_result;
        }
    
        return $results;
    }
    
    private function process_wp_http_response($response, object $link, $is_clean_url = false) {
        $url = $is_clean_url ? $link->clean_url : $link->url;

        $http_code = wp_remote_retrieve_response_code($response);

        $headers = wp_remote_retrieve_headers($response);

        $final_url = $url;

        if ($http_code >= 300 && $http_code < 400 && $headers && $headers->offsetExists('location')) {
            $redirected_to = $headers['location'];
            if (wp_http_validate_url($redirected_to) !== false) {
                $final_url = $redirected_to;
            } else {
                $absolute_redirect_url = wp_normalize_url( trailingslashit( dirname( $url ) ) . ltrim( $redirected_to, '/' ) );
                if (wp_http_validate_url($absolute_redirect_url) !== false) {
                    $final_url = $absolute_redirect_url;
                }
            }
        }

        $response_time = 0;

        $status_text = 'Unknown Status';
        if ($http_code >= 100 && $http_code < 200) {
            $status_text = 'Informational';
        } elseif ($http_code >= 200 && $http_code < 300) {
            $status_text = 'OK';
        } elseif ($http_code >= 300 && $http_code < 400) {
            $status_text = 'Redirection';
        } elseif ($http_code >= 400 && $http_code < 500) {
            $status_text = 'Client Error';
        } elseif ($http_code >= 500) {
            $status_text = 'Server Error';
        }

        $broken = $http_code >= 400 ? 1 : 0;

        $log = 'HTTP Status: ' . $http_code . ' ' . $status_text;
        if ($broken) {
            $log .= ' - Broken Link';
        } elseif ($status_text === 'Redirection') {
            $log .= ' - Redirect';
        }

        $warning = ($http_code >= 300 && $http_code < 400) ||
                ($http_code >= 400 && $http_code != 404) ||
                ($http_code >= 500);

        $is_timeout = 0;

        $may_recheck = 0;

        $result_hash = implode('|', [$http_code, $status_text, $is_timeout, $url]);

        return [
            'post_id' => $link->post_id,
            'anchor' => $link->anchor,
            'parser_type' => 'link',
            'url' => $url,
            'last_checked' => current_time('mysql'),
            'check_count' => 1,
            'final_url' => $final_url,
            'log' => $log,
            'http_code' => $http_code,
            'status_code' => $http_code,
            'status_text' => $status_text,
            'response_time' => $response_time,
            'result_hash' => $result_hash,
            'may_recheck' => $may_recheck,
            'is_timeout' => $is_timeout,
            'is_broken' => $broken,
            'is_warning' => $warning,
            'is_dismissed' => 0,
        ];
    }

    private function update_linksy_broken_link($link, $link_replacement)
    {
        Database::table("linksy_broken_links")
            ->where('url', $link->url)
            ->where('post_id', $link->post_id)
            ->where('anchor', $link->anchor)
            ->set('url', $link_replacement)->set('anchor', $link->anchor)
            ->update();
    }

    private function insert_broken_link($link, $options)
    {
        if (empty($link)) {
            return;
        }

        $record = Database::table("linksy_broken_links")
            ->where('url', $link->clean_url)
            ->where('post_id', $link->post_id)
            ->where('anchor', $link->anchor)
            ->select(['check_count'])->one();

        $current_check_count = isset($record->check_count) ? (int)$record->check_count : 0;

        $query = Database::table("linksy_broken_links")
            ->where('url', $link->clean_url)
            ->where('post_id', $link->post_id)
            ->where('anchor', $link->anchor);

        if (!empty($options)) {
            foreach ($options as $key => $value) {
                if ($key === 'check_count') {
                    continue;
                }

                $query->set($key, $value);
            }
        }

        $query->set('check_count', $current_check_count)->update();
    }

    public function refresh_broken_links($post)
    {
        $post_id = $post->get_ID();
        $data = [];

        $links = Database::table("linksy_links")->where('post_id', $post_id)
            ->select(['post_id', 'clean_url', 'raw_url', 'anchor', 'host'])
            ->get();
            
        $deleted = Database::table("linksy_broken_links")->where('post_id', $post_id)->delete();

        if (!$deleted || empty($links)) {
            return;
        }
        
        foreach ($links as $link) {
            $result = $this->linksy_broken_link_check($link, 12, true);
    
            if (!empty($result) && !empty($result['url'])) {
                $data[] = $result;
            }
        }
    
        $this->insert_multiple_broken_links($data);
    }

    private function insert_multiple_broken_links($links = [])
    {
        $links_cnt = count($links);

        if ($links_cnt > 0) {
            Database::table("linksy_broken_links")->insertMultiple(
                [
                    'post_id',
                    'anchor',
                    'parser_type',
                    'url',
                    'last_checked',
                    'check_count',
                    'final_url',
                    'log',
                    'http_code',
                    'status_code',
                    'status_text',
                    'response_time',
                    'result_hash',
                    'may_recheck',
                    'is_timeout',
                    'is_broken',
                    'is_warning',
                    'is_dismissed',
                ],
                $links
            );
        }
    }
}
