<?php
if (!defined('ABSPATH')) exit;

class WPTA_Crawler {
    private $limit;
    private $visited = array();
    private $queue = array();
    private $results = array();

    public function __construct($limit = 25) {
        $this->limit = max(1, min(absint($limit), 500));
    }

    public function collect_seed_urls() {
        $urls = array(home_url('/'));
        $post_types = get_post_types(array('public' => true), 'names');
        foreach ($post_types as $type) {
            if (in_array($type, array('attachment'), true)) continue;
            $posts = get_posts(array(
                'post_type' => $type,
                'post_status' => 'publish',
                'posts_per_page' => min(50, $this->limit),
                'orderby' => 'modified',
                'order' => 'DESC',
                'fields' => 'ids'
            ));
            foreach ($posts as $id) {
                $urls[] = get_permalink($id);
            }
        }
        $taxonomies = get_taxonomies(array('public' => true), 'names');
        foreach ($taxonomies as $taxonomy) {
            $terms = get_terms(array('taxonomy' => $taxonomy, 'hide_empty' => false, 'number' => 20));
            if (is_wp_error($terms)) continue;
            foreach ($terms as $term) {
                $link = get_term_link($term);
                if (!is_wp_error($link)) $urls[] = $link;
            }
        }
        return array_values(array_unique(array_filter(array_map(array('WPTA_Utils', 'normalize_url'), $urls))));
    }

    public function crawl() {
        $this->queue = $this->collect_seed_urls();
        while (!empty($this->queue) && count($this->visited) < $this->limit) {
            $url = array_shift($this->queue);
            $url = WPTA_Utils::normalize_url($url);
            if (!$url || isset($this->visited[$url])) continue;
            if (!WPTA_Utils::is_internal_url($url)) continue;
            $this->visited[$url] = true;
            $response = WPTA_Utils::wp_remote_get_safe($url);
            $status = is_wp_error($response) ? 0 : wp_remote_retrieve_response_code($response);
            $headers = is_wp_error($response) ? array() : wp_remote_retrieve_headers($response);
            $body = is_wp_error($response) ? '' : wp_remote_retrieve_body($response);
            $result = array(
                'url' => $url,
                'status' => $status,
                'headers' => $headers,
                'html' => $body,
                'error' => is_wp_error($response) ? $response->get_error_message() : ''
            );
            $this->results[] = $result;
            if ($status >= 200 && $status < 400 && $body && count($this->visited) < $this->limit) {
                $this->discover_links($body, $url);
            }
        }
        return $this->results;
    }

    private function discover_links($html, $base) {
        $links = WPTA_Utils::matches_all('/<a\s[^>]*href=["\']([^"\']+)["\'][^>]*>/is', $html);
        foreach ($links as $m) {
            $href = WPTA_Utils::absolute_url($m[1], $base);
            if (!$href || !preg_match('#^https?://#i', $href)) continue;
            $href = WPTA_Utils::normalize_url(strtok($href, '#'));
            if (!WPTA_Utils::is_internal_url($href)) continue;
            if (!isset($this->visited[$href]) && !in_array($href, $this->queue, true) && count($this->queue) < $this->limit * 3) {
                $this->queue[] = $href;
            }
        }
    }
}
