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

/**
 * Get the WPML frontend domain for the current language.
 *
 * @return string|null The domain for the current language or null if WPML is not active
 */
function get_wpml_frontend_domain_for_current_lang() {
    // Check if WPML is active using more common detection methods
    if ( defined('WPML_PLUGIN_PATH') || function_exists('icl_get_languages') ) {
        $current_lang = isset($_GET['lang']) ? sanitize_text_field($_GET['lang']) : null;

        if ( ! $current_lang ) {
            $current_lang = apply_filters( 'wpml_admin_language', null );
        }
        if ( ! $current_lang ) {
            $current_lang = apply_filters( 'wpml_current_language', null );
        }

        // Try different WPML domain functions
        if ( function_exists('wpml_get_domain_for_language') ) {
            return wpml_get_domain_for_language( $current_lang );
        }
        
        // Alternative method using WPML settings
        if ( $current_lang && function_exists('icl_get_languages') ) {
            $languages = icl_get_languages('skip_missing=0');
            if ( isset($languages[$current_lang]) && !empty($languages[$current_lang]['url']) ) {
                return $languages[$current_lang]['url'];
            }
        }
    }

    return null;
}

/**
 * Get the current domain identifier from the Morningscore API.
 * Uses WPML frontend domain detection when available.
 *
 * @return string|WP_Error Domain identifier or error
 */
function rank_get_domain_identifier() {
    // Get current domain - try WPML frontend domain first
    $wpml_domain = get_wpml_frontend_domain_for_current_lang();
    
    if ( $wpml_domain ) {
        $site_domain = parse_url( $wpml_domain, PHP_URL_HOST );
        $domain_source = 'WPML frontend domain';
    } else {
        $site_domain = parse_url( home_url(), PHP_URL_HOST );
        $domain_source = 'WordPress home_url()';
    }
    
    
    // Fetch domains from API (no caching)
    $response = wp_remote_get( 'https://api.morningscore.io/v1/domains', array(
        'headers' => array(
            'Authorization' => 'Bearer ' . rank_get_api_key(),
            'Accept' => 'application/json',
        ),
        'timeout' => 15
    ));

    if ( is_wp_error( $response ) ) {
        return $response;
    }

    $response_code = wp_remote_retrieve_response_code( $response );
    if ( $response_code !== 200 ) {
        return new WP_Error(
            'api_error',
            'Failed to retrieve domains. Status code: ' . $response_code
        );
    }

    $domains = json_decode( wp_remote_retrieve_body( $response ), true );
    
    if ( ! is_array( $domains ) ) {
        return new WP_Error( 'api_error', 'Invalid response format when fetching domains' );
    }

    // Find the matching domain
    $domain_identifier = null;
    
    // Normalize the site domain by removing www if present
    $normalized_site_domain = preg_replace( '/^www\./', '', $site_domain );
    
    foreach ( $domains as $domain ) {
        if ( isset( $domain['domain'] ) ) {
            // Normalize the API domain by removing www if present
            $normalized_api_domain = preg_replace( '/^www\./', '', $domain['domain'] );
            
            // Compare the normalized domains
            if ( $normalized_api_domain === $normalized_site_domain ) {
                $domain_identifier = $domain['global_domain_identifier'];
                break;
            }
        }
    }

    if ( ! $domain_identifier ) {
        return new WP_Error( 'domain_not_found', 'Your website domain was not found in your Morningscore account' );
    }

    // No caching - always return fresh domain identifier
    return $domain_identifier;
}

/**
 * Get the AI credits status from the Morningscore API.
 * 
 * @return array|WP_Error Array with credit info or error
 */
function rank_get_ai_credits() {
    $response = wp_remote_get( 
        'https://api.morningscore.io/v1/credits/get-status?credit_type=credits.ai.monthly_ai_credits', 
        array(
            'headers' => array(
                'Authorization' => 'Bearer ' . rank_get_api_key(),
                'Accept' => 'application/json',
            ),
            'timeout' => 15
        )
    );

    if ( is_wp_error( $response ) ) {
        return $response;
    }

    $response_code = wp_remote_retrieve_response_code( $response );
    if ( $response_code !== 200 ) {
        return new WP_Error( 
            'api_error', 
            'Failed to retrieve AI credits. Status code: ' . $response_code
        );
    }

    $credits = json_decode( wp_remote_retrieve_body( $response ), true );
    
    if ( ! is_array( $credits ) || ! isset( $credits['limit'] ) || ! isset( $credits['usage'] ) ) {
        return new WP_Error( 'api_error', 'Invalid response format when fetching AI credits' );
    }

    return $credits;
}


/**
 * Get all issues for a specific health check category.
 * Filters out issues with URLs that don't exist in WordPress.
 *
 * @param string $issue_type The category key (e.g., 'missing_alt_tags')
 * @param int $limit Optional limit for number of issues to return
 * @param int $offset Optional offset for pagination
 * @return array|WP_Error Array of issues or error
 */
function rank_get_all_issues( $issue_type, $limit = null, $offset = null ) {
    $domain_identifier = rank_get_domain_identifier();
    if ( is_wp_error( $domain_identifier ) ) {
        return $domain_identifier;
    }

    // If limit and offset are provided, use chunked approach
    if ( $limit !== null && $offset !== null ) {
        return rank_get_issues_chunk( $issue_type, $limit, $offset, $domain_identifier );
    }

    // Start with first page to get total count
    $per_page = 100;
    $all_issues = array();
    $skipped_count = 0;
    $count = 0;
    $total_pages = 1;

    // Fetch all pages
    for ( $page = 1; $page <= $total_pages; $page++ ) {
        $url = "https://api.morningscore.io/v1/{$domain_identifier}/get_issues";
        $query_args = array(
            'filter.issue_type' => $issue_type,
            'filter.resolved' => 'false',
            'filter.ignored' => 'false',
            'pagination.page' => $page,
            'pagination.per_page' => $per_page
        );

        $response = wp_remote_get(
            add_query_arg( $query_args, $url ),
            array(
                'headers' => array(
                    'Authorization' => 'Bearer ' . rank_get_api_key(),
                    'Accept' => 'application/json',
                ),
                'timeout' => 60
            )
        );

        if ( is_wp_error( $response ) ) {
            return $response;
        }

        $response_code = wp_remote_retrieve_response_code( $response );
        if ( $response_code !== 200 ) {
            return new WP_Error(
                'api_error',
                'Failed to retrieve issues. Status code: ' . $response_code
            );
        }

        $result = json_decode( wp_remote_retrieve_body( $response ), true );
        
        if ( ! is_array( $result ) || ! isset( $result['data'] ) || ! is_array( $result['data'] ) ) {
            return new WP_Error( 'api_error', 'Invalid response format when fetching issues' );
        }

        // Get count from first response and calculate total pages
        if ( $page === 1 ) {
            if ( ! isset( $result['total'] ) ) {
                return new WP_Error( 'api_error', 'Invalid response format - missing total count' );
            }
            $count = intval( $result['total'] );
            $total_pages = ceil( $count / $per_page );
            
            // If no issues found, return empty array
            if ( $count === 0 ) {
                return array();
            }
        }

        // Filter issues to only include those with URLs that exist in WordPress and don't redirect
        foreach ($result['data'] as $issue) {
            // Get the page URL from the issue data
            $page_url = null;
            if (isset($issue['page']) && isset($issue['page']['url'])) {
                $page_url = $issue['page']['url'];
            } elseif (isset($issue['url'])) {
                $page_url = $issue['url'];
            }

            if (!empty($page_url)) {
                // Check if the URL exists and doesn't redirect
                $url_validation = rank_validate_url($page_url);
                $url_exists = $url_validation['exists'];
                
                if ($url_exists) {
                    // URL exists and doesn't redirect, add the issue to the list
                    $all_issues[] = $issue;
                } else {
                    // URL doesn't exist or redirects, skip this issue
                    $skipped_count++;
                }
            } else {
                // No URL found in the issue data, add it anyway
                $all_issues[] = $issue;
            }
        }

        // Add a short delay between requests
        if ( $page < $total_pages - 1 ) {
            sleep( 0.2 );
        }
    }


    return $all_issues;
}

/**
 * Get a chunk of issues for pagination.
 *
 * @param string $issue_type The issue type to fetch
 * @param int $limit Number of issues to return
 * @param int $offset Starting offset
 * @param string $domain_identifier Domain identifier
 * @return array Array with 'issues' and 'total' keys
 */
function rank_get_issues_chunk( $issue_type, $limit, $offset, $domain_identifier ) {
    // Calculate which API pages we need to fetch
    $api_per_page = 100;
    $start_page = floor( $offset / $api_per_page ) + 1;
    $end_page = ceil( ( $offset + $limit ) / $api_per_page );
    
    $all_issues = array();
    $skipped_count = 0;
    $total_count = 0;
    
    // Fetch the required API pages
    for ( $page = $start_page; $page <= $end_page; $page++ ) {
        $url = "https://api.morningscore.io/v1/{$domain_identifier}/get_issues";
        $query_args = array(
            'filter.issue_type' => $issue_type,
            'filter.resolved' => 'false',
            'filter.ignored' => 'false',
            'pagination.page' => $page,
            'pagination.per_page' => $api_per_page
        );

        $response = wp_remote_get(
            add_query_arg( $query_args, $url ),
            array(
                'headers' => array(
                    'Authorization' => 'Bearer ' . rank_get_api_key(),
                    'Accept' => 'application/json',
                ),
                'timeout' => 60
            )
        );

        if ( is_wp_error( $response ) ) {
            return $response;
        }

        $response_code = wp_remote_retrieve_response_code( $response );
        if ( $response_code !== 200 ) {
            return new WP_Error(
                'api_error',
                'Failed to retrieve issues. Status code: ' . $response_code
            );
        }

        $result = json_decode( wp_remote_retrieve_body( $response ), true );
        
        if ( ! is_array( $result ) || ! isset( $result['data'] ) || ! is_array( $result['data'] ) ) {
            return new WP_Error( 'api_error', 'Invalid response format when fetching issues' );
        }

        // Capture total count from first page response
        if ( $page === $start_page && isset( $result['total'] ) ) {
            $total_count = intval( $result['total'] );
        }

        // Filter issues to only include those with URLs that exist in WordPress and don't redirect
        foreach ($result['data'] as $issue) {
            // Get the page URL from the issue data
            $page_url = null;
            if (isset($issue['page']) && isset($issue['page']['url'])) {
                $page_url = $issue['page']['url'];
            } elseif (isset($issue['url'])) {
                $page_url = $issue['url'];
            }

            if (!empty($page_url)) {
                // Check if the URL exists and doesn't redirect
                $url_validation = rank_validate_url($page_url);
                $url_exists = $url_validation['exists'];
                
                if ($url_exists) {
                    // URL exists and doesn't redirect, add the issue to the list
                    $all_issues[] = $issue;
                } else {
                    // URL doesn't exist or redirects, skip this issue
                    $skipped_count++;
                }
            } else {
                // No URL found in the issue data, add it anyway
                $all_issues[] = $issue;
            }
        }
    }
    
    // Apply offset and limit to the filtered results
    $chunk_start = $offset % $api_per_page;
    if ( $start_page < $end_page ) {
        // We fetched multiple pages, need to calculate the correct offset
        $chunk_start = $offset - ( ( $start_page - 1 ) * $api_per_page );
    }
    
    $result_issues = array_slice( $all_issues, $chunk_start, $limit );
    
    // Always return both issues and total count since every API response includes the total
    return array(
        'issues' => $result_issues,
        'total' => $total_count
    );
}

/**
 * Get AI fix for a specific issue.
 *
 * @param string $rank_id The rank ID of the issue
 * @param string $domain_identifier Optional domain identifier (if not provided, it will be fetched)
 * @return array|WP_Error The AI fix or error
 */
function rank_get_ai_fix( $rank_id, $domain_identifier = null ) {
    if ( ! $domain_identifier ) {
        $domain_identifier = rank_get_domain_identifier();
        if ( is_wp_error( $domain_identifier ) ) {
            return $domain_identifier;
        }
    }

    $url = "https://api.morningscore.io/v1/{$domain_identifier}/get_ai_fix";
    
    $request_body = array(
        'rank_id' => $rank_id,
        'fresh_data' => null
    );
    
    // Initial API call to get task_id
    $response = wp_remote_post(
        $url,
        array(
            'headers' => array(
                'Authorization' => 'Bearer ' . rank_get_api_key(),
                'Content-Type' => 'application/json',
                'Accept' => 'application/json',
            ),
            'body' => wp_json_encode($request_body),
            'timeout' => 15 // Reduced timeout since we're just getting a task ID
        )
    );

    if ( is_wp_error( $response ) ) {
        return $response;
    }

    $response_code = wp_remote_retrieve_response_code( $response );
    $response_body = wp_remote_retrieve_body( $response );
    
    $decoded_body = json_decode( $response_body, true );

    if ( $response_code !== 200 ) {
        // Specifically check for "OUT_OF_CREDITS"
        if ( isset( $decoded_body['message'] ) && $decoded_body['message'] === 'OUT_OF_CREDITS' ) {
            return new WP_Error(
                'out_of_credits',
                'AI credits exhausted. API Message: OUT_OF_CREDITS',
                array( 'status_code' => $response_code ) // Pass status code
            );
        }
        
        // Handle other API errors
        $error_message = isset( $decoded_body['message'] ) ? $decoded_body['message'] : 'Failed to retrieve AI fix.';
        return new WP_Error(
            'api_error',
            "Failed to retrieve AI fix. Status code: $response_code. Message: $error_message",
            array( 'status_code' => $response_code ) // Pass status code
        );
    }

    // Check if we have a task_id in the response
    if ( ! is_array( $decoded_body ) || ! isset( $decoded_body['task_id'] ) ) {
        return new WP_Error(
            'api_error',
            'Invalid response format when fetching AI fix - missing task_id.',
            array( 'status_code' => $response_code )
        );
    }

    $task_id = $decoded_body['task_id'];
    
    // Poll for the result
    $result = rank_get_ai_fix_status($task_id);
    
    return $result;
}

/**
 * Poll for AI fix status until completion or timeout.
 *
 * @param string $task_id The task ID to check
 * @param int $max_attempts Maximum number of polling attempts (default: 18 - covers ~35 seconds with 2-second intervals)
 * @param int $poll_interval Interval between polls in seconds (default: 2)
 * @return array|WP_Error The AI fix result or error
 */
function rank_get_ai_fix_status($task_id, $max_attempts = 18, $poll_interval = 2) {
    // Correct endpoint URL format for status check
    $status_url = "https://api.morningscore.io/v1/get_ai_fix_status/{$task_id}";
    
    for ($attempt = 1; $attempt <= $max_attempts; $attempt++) {
        // Add retry logic for each poll attempt
        $poll_result = rank_api_request_with_retry($status_url, 3);
        
        if (is_wp_error($poll_result)) {
            return $poll_result;
        }
        
        $response_code = $poll_result['response_code'];
        $result = $poll_result['data'];
        
        if ($response_code !== 200) {
            return new WP_Error(
                'api_error',
                "Failed to retrieve AI fix status. Status code: $response_code.",
                array('status_code' => $response_code)
            );
        }
        
        if (!is_array($result)) {
            return new WP_Error(
                'api_error',
                'Invalid response format when fetching AI fix status.',
                array('status_code' => $response_code)
            );
        }

        // Check if the task is completed or failed
        if (isset($result['status'])) {
            if ($result['status'] === 'completed') {
                // Format the response to match the expected structure
                if (isset($result['data']) && isset($result['data']['content'])) {
                    return array(
                        'content' => $result['data']['content'],
                        'progress' => $result['data']['progress'],
                        'task_id' => $result['data']['task_id'],
                        'identifier' => $result['data']['identifier']
                    );
                } else {
                    // Extract any error message from the response
                    $error_message = 'The AI had an error. Skipping this fix.';
                    
                    // Check if there's a more specific message in the response
                    if (isset($result['data']['message'])) {
                        $error_message = $result['data']['message'];
                    } elseif (isset($result['message'])) {
                        $error_message = $result['message'];
                    } elseif (isset($result['data']['error'])) {
                        $error_message = $result['data']['error'];
                    } elseif (isset($result['error'])) {
                        $error_message = $result['error'];
                    }
                    
                    return new WP_Error(
                        'api_error',
                        $error_message,
                        array('status_code' => $response_code, 'response' => $result)
                    );
                }
            } elseif ($result['status'] === 'failed') {
                // Handle failed status explicitly
                // When status is 'failed', we stop polling immediately as there's nothing more to do
                $error_message = 'RANK AI Could not find a fix';
                
                // Check if there's a more specific message in the response
                if (isset($result['data']['message'])) {
                    $error_message = $result['data']['message'];
                } elseif (isset($result['message'])) {
                    $error_message = $result['message'];
                } elseif (isset($result['data']['error'])) {
                    $error_message = $result['data']['error'];
                } elseif (isset($result['error'])) {
                    $error_message = $result['error'];
                }
                
                // Return error and stop polling - this exits the function and breaks the polling loop
                return new WP_Error(
                    'api_failed',
                    $error_message,
                    array('status_code' => $response_code, 'response' => $result)
                );
            }
        }

        // If not completed, wait before polling again
        if ($attempt < $max_attempts) {
            sleep($poll_interval);
        }
    }

    // If we've reached the maximum number of attempts without completion
    $timeout_seconds = $max_attempts * $poll_interval;
    return new WP_Error(
        'api_timeout',
        "Timed out waiting for AI fix to complete after {$timeout_seconds} seconds. The API did not return a completed or failed status.",
        array('task_id' => $task_id)
    );
}

/**
 * Make an API request with retry logic.
 *
 * @param string $url The URL to request
 * @param int $max_retries Maximum number of retries (default: 3)
 * @param array $args Additional arguments for wp_remote_get
 * @return array|WP_Error The response data or error
 */
function rank_api_request_with_retry($url, $max_retries = 3, $args = array()) {
    $default_args = array(
        'headers' => array(
            'Authorization' => 'Bearer ' . rank_get_api_key(),
            'Accept' => 'application/json',
        ),
        'timeout' => 60
    );
    
    $request_args = wp_parse_args($args, $default_args);
    
    $retry_count = 0;
    $last_error = null;
    
    while ($retry_count < $max_retries) {
        $response = wp_remote_get($url, $request_args);
        
        if (!is_wp_error($response)) {
            $response_code = wp_remote_retrieve_response_code($response);
            $response_body = wp_remote_retrieve_body($response);
            $decoded_body = json_decode($response_body, true);
            
            // If we got a valid response, return it
            if ($response_code === 200 && $decoded_body) {
                return array(
                    'response_code' => $response_code,
                    'data' => $decoded_body
                );
            }
        }
        
        // Store the last error
        $last_error = is_wp_error($response) ? $response : new WP_Error(
            'api_error',
            "API request failed with status code: $response_code",
            array('status_code' => $response_code)
        );
        
        // Increment retry count and wait before retrying
        $retry_count++;
        if ($retry_count < $max_retries) {
            sleep(1); // Wait 1 second between retries
        }
    }
    
    // If we've exhausted all retries, return the last error
    return $last_error;
}

/**
 * Check if an issue has already been fixed locally.
 *
 * @param string $issue_type The type of issue (e.g., 'missing_alt_tags')
 * @param string $page_url The URL of the page with the issue
 * @param string $issue_identifier The specific identifier for the issue (e.g., image URL)
 * @return bool True if the issue has already been fixed locally
 */
function rank_is_issue_already_fixed($issue_type, $page_url, $issue_identifier) {
    // Static caches to avoid repeated lookups and database queries
    static $attachment_id_cache = [];
    static $fixed_status_cache = [];
    static $post_id_cache = [];
    static $option_cache = [];
    
    // Debug log the function call
    // Check if issue is fixed
    
    // Create a unique cache key for this check
    $cache_key = md5($issue_type . '|' . $page_url . '|' . $issue_identifier);
    
    // Check if we already have a cached result
    if (isset($fixed_status_cache[$cache_key])) {
        // Using cached result
        return $fixed_status_cache[$cache_key];
    }
    
    // Different logic based on issue type
    $issue_config = rank_get_issue_type_config($issue_type);
    
    if (!$issue_config) {
        // No issue config found
        $fixed_status_cache[$cache_key] = false;
        return false;
    }
    
    // Process issue config
    
    // Get post ID from URL (with caching)
    if (isset($post_id_cache[$page_url])) {
        $post_id = $post_id_cache[$page_url];
    } else {
        $post_id = url_to_postid($page_url);
        if (!$post_id) {
            // Try enhanced URL to post ID conversion
            $post_id = Rank_Processor::enhanced_url_to_postid($page_url);
        }
        $post_id_cache[$page_url] = $post_id;
    }
    
    if ($issue_config['handler_type'] === 'database_override') {
        global $wpdb;
        $table_name = $wpdb->prefix . RANK_TABLE_NAME;
        $db_override_type = '';
        
        // Map issue type to database override type
        switch ($issue_type) {
            case 'missing_meta_description':
            case 'short_meta_description':
            case 'long_meta_description':
                $db_override_type = 'meta_description';
                break;
            case 'missing_title_tag':
            case 'short_title_tag':
            case 'long_title_tag':
                $db_override_type = 'title';
                break;
            case 'missing_alt_tags':
                $db_override_type = 'image_alt';
                
                // For alt tags, use a more efficient approach
                if ($post_id > 0) {
                    // First check if this post has any alt tag overrides at all
                    // This is much faster than looking up each attachment ID
                    static $post_has_alt_overrides = [];
                    
                    if (!isset($post_has_alt_overrides[$post_id])) {
                        $query = $wpdb->prepare(
                            "SELECT COUNT(*) FROM `$table_name` WHERE post_id = %d AND override_type = %s LIMIT 1",
                            $post_id, $db_override_type
                        );
                        $count = $wpdb->get_var($query);
                        $post_has_alt_overrides[$post_id] = ($count > 0);
                    }
                    
                    // If the post has any alt tag overrides, we'll consider the issue fixed
                    // This is a simplification that avoids the expensive attachment ID lookup
                    if ($post_has_alt_overrides[$post_id]) {
                        $fixed_status_cache[$cache_key] = true;
                        return true;
                    }
                    
                    // Only if we need exact matching, do the more expensive lookup
                    if ($issue_config['identifier_is_url'] && !empty($issue_identifier) && defined('RANK_EXACT_ALT_MATCHING') && RANK_EXACT_ALT_MATCHING) {
                        // Use cached attachment ID if available
                        if (!isset($attachment_id_cache[$issue_identifier])) {
                            $attachment_id_cache[$issue_identifier] = Rank_Processor::get_attachment_id_from_url($issue_identifier);
                        }
                        $object_id = $attachment_id_cache[$issue_identifier];
                        
                        if ($object_id > 0) {
                            // Check if this specific image alt tag has been fixed
                            $query = $wpdb->prepare(
                                "SELECT COUNT(*) FROM `$table_name` WHERE post_id = %d AND object_id = %d AND override_type = %s",
                                $post_id, $object_id, $db_override_type
                            );
                            $count = $wpdb->get_var($query);
                            $fixed_status_cache[$cache_key] = ($count > 0);
                            return $fixed_status_cache[$cache_key];
                        }
                    }
                }
                
                $fixed_status_cache[$cache_key] = false;
                return false;
        }
        
        // For non-image issues or if image ID wasn't found
        if ($post_id > 0 && !empty($db_override_type)) {
            $query = $wpdb->prepare(
                "SELECT COUNT(*) FROM `$table_name` WHERE post_id = %d AND override_type = %s",
                $post_id, $db_override_type
            );
            $count = $wpdb->get_var($query);
            // Database query executed
            $fixed_status_cache[$cache_key] = ($count > 0);
            return $fixed_status_cache[$cache_key];
        } else {
            // Skipping database query
        }
    }
    elseif ($issue_config['handler_type'] === 'option_suggestion') {
        // For broken links
        $option_name = '';
        if ($issue_type === 'broken_links_internal') {
            $option_name = 'rank_internal_url_replacements';
        } elseif ($issue_type === 'broken_links_external') {
            $option_name = 'rank_external_url_replacements';
        }
        
        // Checking option suggestion
        
        if (!empty($option_name) && !empty($issue_identifier)) {
            // Cache option values to avoid repeated get_option calls
            if (!isset($option_cache[$option_name])) {
                $option_cache[$option_name] = get_option($option_name, []);
                // Option loaded
            }
            $replacements = $option_cache[$option_name];
            
            // Check if this specific URL has a replacement
            $result = (is_array($replacements) && isset($replacements[$issue_identifier]));
            // URL replacement check completed
            $fixed_status_cache[$cache_key] = $result;
            return $result;
        } else {
            // Skipping option check
        }
    }
    
    $fixed_status_cache[$cache_key] = false;
    return false;
}

/**
 * Get health issue counts from the new /overall endpoint.
 * Simple and fast - no sampling or fix status checking.
 *
 * @return array|WP_Error Array with issue counts by category and crawled_at timestamp or error
 */
function rank_get_health_issues_overview_counts() {
    $domain_identifier = rank_get_domain_identifier();
    if (is_wp_error($domain_identifier)) {
        return $domain_identifier;
    }
    
    // Single API call to get all issue counts
    $url = "https://api.morningscore.io/v1/{$domain_identifier}/overall";
    $response = wp_remote_get($url, array(
        'headers' => array(
            'Authorization' => 'Bearer ' . rank_get_api_key(),
            'Accept' => 'application/json',
        ),
        'timeout' => 30
    ));
    
    if (is_wp_error($response)) {
        return $response;
    }
    
    $response_code = wp_remote_retrieve_response_code($response);
    if ($response_code !== 200) {
        return new WP_Error('api_error', 'Failed to retrieve overview. Status code: ' . $response_code);
    }
    
    $result = json_decode(wp_remote_retrieve_body($response), true);
    if (!is_array($result)) {
        return new WP_Error('api_error', 'Invalid overview response format - not an array');
    }
    
    // Check if we have the expected structure
    if (!isset($result['categories'])) {
        return new WP_Error('api_error', 'Invalid overview response format - missing categories');
    }
    
    // Extract crawled_at timestamp
    $crawled_at = isset($result['crawled_at']) ? $result['crawled_at'] : null;
    
    // Get all known categories for labels
    $all_categories = rank_get_health_check_categories();
    $issue_counts = array();
    
    // Process each category's problems
    foreach ($result['categories'] as $category => $category_data) {
        if (!isset($category_data['problems']) || !is_array($category_data['problems'])) {
            continue;
        }
        
        foreach ($category_data['problems'] as $problem) {
            $validator = $problem['validator'];
            $issues_count = intval($problem['issues_num']);
            
            // Handle empty validator field
            if (empty($validator)) {
                continue; // Skip empty validators
            }
            
            // Map validator to internal issue type (direct mapping)
            $issue_type = $validator;
            $label = isset($all_categories[$issue_type]) ? $all_categories[$issue_type] : $issue_type;
            
            $issue_counts[$issue_type] = array(
                'label' => $label,
                'count' => $issues_count < 0 ? 0 : $issues_count
            );
        }
    }
    
    // Ensure all known issue types are included (even if not in API response)
    foreach ($all_categories as $issue_type => $label) {
        if (!isset($issue_counts[$issue_type])) {
            $issue_counts[$issue_type] = array(
                'label' => $label,
                'count' => 0
            );
        }
    }
    
    return array(
        'issues' => $issue_counts,
        'crawled_at' => $crawled_at
    );
}



/**
 * Check if the crawled_at timestamp is older than 30 days.
 *
 * @param string $crawled_at ISO 8601 timestamp string (e.g., "2025-07-09T09:54:04.016Z")
 * @return bool True if older than 30 days, false otherwise
 */
function rank_is_crawl_data_too_old($crawled_at) {
    if (empty($crawled_at)) {
        return false; // Consider missing timestamp as fresh
    }
    
    // Parse the ISO 8601 timestamp
    $crawled_timestamp = strtotime($crawled_at);
    if ($crawled_timestamp === false) {
        return false; // Invalid timestamp format, consider fresh
    }
    
    // Calculate 30 days ago from now
    $thirty_days_ago = time() - (30 * 24 * 60 * 60);
    
    // Return true if crawled_at is older than 30 days
    return $crawled_timestamp < $thirty_days_ago;
}


/**
 * Check if a URL is accessible and doesn't redirect.
 *
 * @param string $url The URL to check
 * @return array Result with 'exists' and 'redirects' flags
 */
function rank_validate_url($url) {
    // Use static cache to avoid checking the same URL multiple times
    static $url_cache = array();
    
    // Return cached result if available
    if (isset($url_cache[$url])) {
        Rank_Processor::add_debug_log("URL validation cache hit for: $url");
        return $url_cache[$url];
    }
    
    Rank_Processor::add_debug_log("=== URL VALIDATION START ===");
    Rank_Processor::add_debug_log("Validating URL: $url");
    
    $result = array(
        'exists' => false,
        'redirects' => false,
        'status' => 0,
        'method_used' => ''
    );
    
    // Step 1: Try HEAD request first (fast check)
    Rank_Processor::add_debug_log("Attempting HEAD request...");
    $head_response = wp_remote_head($url, array(
        'timeout' => 10, // Increased timeout
        'redirection' => 0, // Don't follow redirects
        'user-agent' => 'RANK AI WordPress Plugin/1.0',
        'sslverify' => false // Disable SSL verification to handle sites with certificate issues
    ));
    
    if (is_wp_error($head_response)) {
        $error_message = $head_response->get_error_message();
        Rank_Processor::add_debug_log("HEAD request failed: $error_message");
    } else {
        $head_status = wp_remote_retrieve_response_code($head_response);
        $head_headers = wp_remote_retrieve_headers($head_response);
        
        Rank_Processor::add_debug_log("HEAD response status: $head_status");
        Rank_Processor::add_debug_log("HEAD response headers: " . print_r($head_headers->getAll(), true));
        
        // Check if HEAD request was successful
        if ($head_status >= 200 && $head_status < 300) {
            $result['exists'] = true;
            $result['status'] = $head_status;
            $result['method_used'] = 'HEAD';
            
            Rank_Processor::add_debug_log("HEAD request successful - URL exists");
            Rank_Processor::add_debug_log("=== URL VALIDATION END (HEAD SUCCESS) ===");
            
            // Cache and return successful result
            $url_cache[$url] = $result;
            return $result;
        } else if ($head_status >= 300 && $head_status < 400) {
            $result['redirects'] = true;
            $result['status'] = $head_status;
            Rank_Processor::add_debug_log("HEAD request shows redirect (status: $head_status)");
        } else {
            Rank_Processor::add_debug_log("HEAD request unsuccessful (status: $head_status)");
        }
    }
    
    // Step 2: Fallback to GET request if HEAD failed or was inconclusive
    Rank_Processor::add_debug_log("Attempting GET request fallback...");
    $get_response = wp_remote_get($url, array(
        'timeout' => 10,
        'redirection' => 0, // Don't follow redirects
        'user-agent' => 'RANK AI WordPress Plugin/1.0',
        'sslverify' => false // Disable SSL verification to handle sites with certificate issues
    ));
    
    if (is_wp_error($get_response)) {
        $error_message = $get_response->get_error_message();
        Rank_Processor::add_debug_log("GET request failed: $error_message");
        $result['exists'] = false;
        $result['method_used'] = 'GET_FAILED';
    } else {
        $get_status = wp_remote_retrieve_response_code($get_response);
        $get_headers = wp_remote_retrieve_headers($get_response);
        
        Rank_Processor::add_debug_log("GET response status: $get_status");
        Rank_Processor::add_debug_log("GET response headers: " . print_r($get_headers->getAll(), true));
        
        $result['status'] = $get_status;
        $result['method_used'] = 'GET';
        
        // Check if it's a redirect (300-399)
        if ($get_status >= 300 && $get_status < 400) {
            $result['redirects'] = true;
            $result['exists'] = false;
            Rank_Processor::add_debug_log("GET request shows redirect (status: $get_status) - marking as non-existent");
        }
        // Check if it exists (200-299)
        else if ($get_status >= 200 && $get_status < 300) {
            $result['exists'] = true;
            Rank_Processor::add_debug_log("GET request successful - URL exists");
        }
        // Handle other status codes
        else {
            $result['exists'] = false;
            Rank_Processor::add_debug_log("GET request unsuccessful (status: $get_status) - marking as non-existent");
        }
    }
    
    // Final result logging
    $exists_text = $result['exists'] ? 'EXISTS' : 'DOES NOT EXIST';
    $redirects_text = $result['redirects'] ? 'REDIRECTS' : 'NO REDIRECT';
    Rank_Processor::add_debug_log("Final result: $exists_text, $redirects_text, Status: {$result['status']}, Method: {$result['method_used']}");
    Rank_Processor::add_debug_log("=== URL VALIDATION END ===");
    
    // Cache the result
    $url_cache[$url] = $result;
    
    return $result;
}
