<?php

/**
 * Notifications Feed
 *
 * @package ProRank\SEO\Core\Notifications
 * @since   0.1.0
 */

declare(strict_types=1);

namespace ProRank\SEO\Core\Notifications;

use ProRank\SEO\Core\Config\Settings;
use ProRank\SEO\Core\LicenseManager;
use ProRank\SEO\Core\ModuleManager;

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}


/**
 * Class NotificationsFeed
 *
 * Manages dashboard notifications feed with local and remote notifications
 */


class NotificationsFeed {
    /**
     * Notification types
     */
    const TYPE_INFO = 'info';
    const TYPE_WARNING = 'warning';
    const TYPE_ERROR = 'error';
    const TYPE_SUCCESS = 'success';
    const TYPE_TIP = 'tip';
    const TYPE_UPDATE = 'update';
    const TYPE_PROMO = 'promo';

    /**
     * Notification priorities
     */
    const PRIORITY_LOW = 'low';
    const PRIORITY_MEDIUM = 'medium';
    const PRIORITY_HIGH = 'high';
    const PRIORITY_CRITICAL = 'critical';

    /**
     * License manager instance
     *
     * @var LicenseManager
     */
    private LicenseManager $license_manager;

    /**
     * Module manager instance
     *
     * @var ModuleManager
     */
    private ModuleManager $module_manager;

    /**
     * Transient key for cached notifications
     *
     * @var string
     */
    private string $cache_key = 'prorank_notifications_feed';

    /**
     * Determine whether a notification is about setup/onboarding.
     *
     * @param array $notification Notification payload.
     * @return bool
     */
    private function is_setup_notification(array $notification): bool {
        $id = (string) ($notification['id'] ?? '');
        $title = (string) ($notification['title'] ?? '');
        $message = (string) ($notification['message'] ?? '');

        if ($id === 'welcome-2025' || $id === 'onboarding-pending') {
            return true;
        }

        return (
            stripos($title, 'welcome to prorank seo') !== false ||
            stripos($message, 'quick setup wizard') !== false
        );
    }

    /**
     * Check whether the quick setup flow has already been completed.
     *
     * Support both the canonical wizard state and the legacy onboarding flags.
     *
     * @return bool
     */
    private function is_setup_completed(): bool {
        if ((bool) Settings::get('onboarding_completed', false)) {
            return true;
        }

        if ((bool) get_option('prorank_onboarding_completed', false)) {
            return true;
        }

        $wizard_state = get_option('prorank_wizard_state_server', []);
        if (!is_array($wizard_state)) {
            return false;
        }

        $wizard_data = $wizard_state['wizardData'] ?? [];
        if (!is_array($wizard_data)) {
            return false;
        }

        return !empty($wizard_data['wizardCompleted']) || !empty($wizard_data['completedAt']);
    }

    /**
     * Check whether a local URL is available.
     *
     * Results are cached briefly to avoid repeated same-site requests from the admin.
     *
     * @param string $url URL to check.
     * @return bool
     */
    private function is_local_url_available(string $url): bool {
        $cache_key = 'prorank_notice_url_' . md5($url);
        $cached = get_transient($cache_key);
        if ($cached !== false) {
            return $cached === '1';
        }

        $response = wp_remote_head($url, [
            'timeout' => 5,
            'redirection' => 3,
            'sslverify' => false,
        ]);

        if (is_wp_error($response) || (int) wp_remote_retrieve_response_code($response) < 200 || (int) wp_remote_retrieve_response_code($response) >= 400) {
            $response = wp_remote_get($url, [
                'timeout' => 5,
                'redirection' => 3,
                'sslverify' => false,
            ]);
        }

        $is_available = !is_wp_error($response);
        if ($is_available) {
            $code = (int) wp_remote_retrieve_response_code($response);
            $is_available = $code >= 200 && $code < 400;
        }

        set_transient($cache_key, $is_available ? '1' : '0', 10 * MINUTE_IN_SECONDS);

        return $is_available;
    }

    /**
     * Determine whether the dynamic sitemap endpoint is actually available.
     *
     * @return bool
     */
    private function is_sitemap_available(): bool {
        if (!$this->module_manager->is_module_enabled('sitemaps')) {
            return false;
        }

        $enabled = (bool) Settings::get(
            'sitemaps.enable_xml_sitemap',
            Settings::get('sitemaps_enable_xml_sitemap', false)
        );

        if (!$enabled) {
            return false;
        }

        return $this->is_local_url_available(home_url('/sitemap_index.xml'));
    }

    /**
     * Determine whether robots.txt is available either physically or virtually.
     *
     * @return bool
     */
    private function is_robots_txt_available(): bool {
        return $this->is_local_url_available(home_url('/robots.txt'));
    }

    /**
     * Normalize notification priority into the supported string values.
     *
     * @param mixed $priority Raw priority value.
     * @return string
     */
    private function normalize_priority($priority): string {
        if (in_array($priority, [
            self::PRIORITY_LOW,
            self::PRIORITY_MEDIUM,
            self::PRIORITY_HIGH,
            self::PRIORITY_CRITICAL,
        ], true)) {
            return $priority;
        }

        if (is_numeric($priority)) {
            $priority = (int) $priority;
            if ($priority >= 75) {
                return self::PRIORITY_CRITICAL;
            }
            if ($priority >= 50) {
                return self::PRIORITY_HIGH;
            }
            if ($priority >= 25) {
                return self::PRIORITY_MEDIUM;
            }
        }

        return self::PRIORITY_LOW;
    }

    /**
     * Normalize remote notification payload into the shape used by the admin UI.
     *
     * @param array $notification Remote notification payload.
     * @return array
     */
    private function normalize_notification(array $notification): array {
        $type = (string) ($notification['type'] ?? self::TYPE_INFO);
        if (!in_array($type, [
            self::TYPE_INFO,
            self::TYPE_WARNING,
            self::TYPE_ERROR,
            self::TYPE_SUCCESS,
            self::TYPE_TIP,
            self::TYPE_UPDATE,
            self::TYPE_PROMO,
        ], true)) {
            $type = self::TYPE_INFO;
        }

        $normalized = $notification;
        $normalized['type'] = $type;
        $normalized['priority'] = $this->normalize_priority($notification['priority'] ?? null);
        $normalized['dismissible'] = array_key_exists('dismissible', $notification)
            ? (bool) $notification['dismissible']
            : true;

        $normalized = $this->normalize_setup_notification_links($normalized);

        if (empty($normalized['action']) && !empty($notification['link'])) {
            $normalized['action'] = [
                'label' => $notification['linkText'] ?? __('View', 'prorank-seo'),
                'url'   => $notification['link'],
            ];
        }

        if (empty($normalized['timestamp'])) {
            $normalized['timestamp'] = time();
        }

        return $normalized;
    }

    /**
     * Rewrite legacy setup notification URLs to the actual quick setup route.
     *
     * Older remote notifications still point at the plain dashboard URL.
     * Normalize them here so the CTA lands on the wizard instead of the overview.
     *
     * @param array $notification Notification payload.
     * @return array
     */
    private function normalize_setup_notification_links(array $notification): array {
        if (!$this->is_setup_notification($notification)) {
            return $notification;
        }

        $quick_setup_url = admin_url('admin.php?page=prorank-seo&tab=quick-setup');
        $dashboard_url = admin_url('admin.php?page=prorank-seo');
        $relative_dashboard_url = 'admin.php?page=prorank-seo';

        if (!empty($notification['link'])) {
            $link = (string) $notification['link'];
            if ($link === $dashboard_url || $link === $relative_dashboard_url) {
                $notification['link'] = $quick_setup_url;
            }
        }

        if (!empty($notification['action']) && is_array($notification['action'])) {
            $action_url = (string) ($notification['action']['url'] ?? '');
            if ($action_url === $dashboard_url || $action_url === $relative_dashboard_url) {
                $notification['action']['url'] = $quick_setup_url;
            }
        }

        return $notification;
    }

    /**
     * Get remote feed URL
     *
     * @return string
     */
    private function getFeedUrl(): string {
        return defined('PRORANK_NOTIFICATIONS_ENDPOINT') ? PRORANK_NOTIFICATIONS_ENDPOINT : 'https://prorank.io/api/v1/notifications';
    }

    /**
     * Constructor
     *
     * @param LicenseManager $license_manager License manager instance
     * @param ModuleManager  $module_manager  Module manager instance
     */
    public function __construct(LicenseManager $license_manager, ModuleManager $module_manager) {
        $this->license_manager = $license_manager;
        $this->module_manager = $module_manager;
    }

    /**
     * Get all notifications
     *
     * @param array $filters Optional filters
     * @return array
     */
    public function get_notifications(array $filters = []): array {
        $notifications = [];

        // Get local notifications
        $local_notifications = $this->get_local_notifications();
        $notifications = array_merge($notifications, $local_notifications);

        // Get remote notifications
        $remote_notifications = $this->get_remote_notifications();
        $notifications = array_merge($notifications, $remote_notifications);

        $setup_completed = $this->is_setup_completed();
        $has_local_setup_notice = array_reduce($local_notifications, function ($carry, $notification) {
            return $carry || (($notification['id'] ?? '') === 'onboarding-pending');
        }, false);

        $notifications = array_values(array_filter($notifications, function ($notification) use ($setup_completed, $has_local_setup_notice) {
            if (!$this->is_setup_notification((array) $notification)) {
                return true;
            }

            if ($setup_completed) {
                return false;
            }

            if ($has_local_setup_notice) {
                return (($notification['id'] ?? '') === 'onboarding-pending');
            }

            return true;
        }));

        // Get dismissed notifications
        $dismissed = $this->get_dismissed_notifications();

        // Filter out dismissed notifications
        $notifications = array_filter($notifications, function($notification) use ($dismissed) {
            return !isset($notification['id']) || !in_array($notification['id'], $dismissed, true);
        });

        // Apply filters
        if (!empty($filters['type'])) {
            $notifications = array_filter($notifications, function($notification) use ($filters) {
                return $notification['type'] === $filters['type'];
            });
        }

        if (!empty($filters['priority'])) {
            $notifications = array_filter($notifications, function($notification) use ($filters) {
                return $notification['priority'] === $filters['priority'];
            });
        }

        // Sort by priority and timestamp
        usort($notifications, function($a, $b) {
            $priority_order = [
                self::PRIORITY_CRITICAL => 0,
                self::PRIORITY_HIGH => 1,
                self::PRIORITY_MEDIUM => 2,
                self::PRIORITY_LOW => 3,
            ];

            $a_priority = $priority_order[$a['priority']] ?? 4;
            $b_priority = $priority_order[$b['priority']] ?? 4;

            if ($a_priority !== $b_priority) {
                return $a_priority - $b_priority;
            }

            // Sort by timestamp (newer first)
            return ($b['timestamp'] ?? 0) - ($a['timestamp'] ?? 0);
        });

        return array_values($notifications);
    }

    /**
     * Get local notifications based on system state
     *
     * @return array
     */
    private function get_local_notifications(): array {
        $notifications = [];
        $current_time = time();
        $setup_completed = $this->is_setup_completed();

        // Check for onboarding
        if (!$setup_completed) {
            $notifications[] = [
                'id' => 'onboarding-pending',
                'type' => self::TYPE_INFO,
                'priority' => self::PRIORITY_HIGH,
                'title' => __('Complete ProRank SEO Setup', 'prorank-seo'),
                'message' => __('Get started with ProRank SEO by completing the quick setup wizard.', 'prorank-seo'),
                'action' => [
                    'label' => __('Start Setup', 'prorank-seo'),
                    'url' => admin_url('admin.php?page=prorank-seo&tab=quick-setup'),
                ],
                'dismissible' => false,
                'timestamp' => $current_time,
            ];
        }

        // Check for license expiration
        $license_data = $this->license_manager->get_license_data();
        if ($license_data && !empty($license_data['expires_at'])) {
            $expires_timestamp = strtotime($license_data['expires_at']);
            $days_until_expiry = floor(($expires_timestamp - $current_time) / DAY_IN_SECONDS);

            if ($days_until_expiry <= 7 && $days_until_expiry > 0) {
                $notifications[] = [
                    'id' => 'license-expiring-soon',
                    'type' => self::TYPE_WARNING,
                    'priority' => self::PRIORITY_HIGH,
                    'title' => __('License Expiring Soon', 'prorank-seo'),
                    'message' => sprintf(
/* translators: %1$: value */
                        __('Your ProRank SEO license expires in %1$d days. Renew now to continue receiving updates and support.', 'prorank-seo'),
                        $days_until_expiry
                    ),
                    'action' => [
                        'label' => __('Renew License', 'prorank-seo'),
                        'url' => 'https://prorank.io/account/licenses',
                    ],
                    'dismissible' => true,
                    'timestamp' => $current_time,
                ];
            } elseif ($days_until_expiry <= 0) {
                $notifications[] = [
                    'id' => 'license-expired',
                    'type' => self::TYPE_ERROR,
                    'priority' => self::PRIORITY_CRITICAL,
                    'title' => __('License Expired', 'prorank-seo'),
                    'message' => __('Your ProRank SEO license has expired. Renew now to continue receiving updates and support.', 'prorank-seo'),
                    'action' => [
                        'label' => __('Renew License', 'prorank-seo'),
                        'url' => 'https://prorank.io/account/licenses',
                    ],
                    'dismissible' => false,
                    'timestamp' => $current_time,
                ];
            }
        }

        // Check for missing sitemaps
        if ($this->module_manager->is_module_enabled('sitemaps') && !$this->is_sitemap_available()) {
            $notifications[] = [
                'id' => 'sitemap-missing',
                'type' => self::TYPE_WARNING,
                'priority' => self::PRIORITY_MEDIUM,
                'title' => __('XML Sitemap Not Generated', 'prorank-seo'),
                'message' => __('Your XML sitemap needs to be generated. This helps search engines discover your content.', 'prorank-seo'),
                'action' => [
                    'label' => __('Generate Sitemap', 'prorank-seo'),
                    'url' => admin_url('admin.php?page=prorank-technical-seo#sitemaps'),
                ],
                'dismissible' => true,
                'timestamp' => $current_time,
            ];
        }

        // Check for missing robots.txt
        if (!$this->is_robots_txt_available()) {
            $notifications[] = [
                'id' => 'robots-txt-missing',
                'type' => self::TYPE_TIP,
                'priority' => self::PRIORITY_LOW,
                'title' => __('No Robots.txt File', 'prorank-seo'),
                'message' => __('Consider creating a robots.txt file to control how search engines crawl your site.', 'prorank-seo'),
                'action' => [
                    'label' => __('Configure Robots.txt', 'prorank-seo'),
                        'url' => admin_url('admin.php?page=prorank-technical-seo#robots-indexing'),
                    ],
                    'dismissible' => true,
                    'timestamp' => $current_time,
                ];
        }

        // Check for plugin updates
        $update_data = get_site_transient('update_plugins');
        if ($update_data && isset($update_data->response['prorank-seo/prorank-seo.php'])) {
            $notifications[] = [
                'id' => 'plugin-update-available',
                'type' => self::TYPE_UPDATE,
                'priority' => self::PRIORITY_MEDIUM,
                'title' => __('ProRank SEO Update Available', 'prorank-seo'),
                'message' => sprintf(
/* translators: %1$: value */
                    __('Version %1$s is available. Update now to get the latest features and improvements.', 'prorank-seo'),
                    $update_data->response['prorank-seo/prorank-seo.php']->new_version
                ),
                'action' => [
                    'label' => __('Update Now', 'prorank-seo'),
                    'url' => admin_url('plugins.php'),
                ],
                'dismissible' => true,
                'timestamp' => $current_time,
            ];
        }

        // Check for disabled critical modules
        $critical_modules = ['meta', 'sitemaps'];
        foreach ($critical_modules as $module_slug) {
            if ($this->module_manager->has_module($module_slug) && !$this->module_manager->is_module_enabled($module_slug)) {
                $module = $this->module_manager->get_module($module_slug);
                if ($module) {
                    $notifications[] = [
                        'id' => 'module-disabled-' . $module_slug,
                        'type' => self::TYPE_TIP,
                        'priority' => self::PRIORITY_MEDIUM,
/* translators: %1$: value */
                        'title' => sprintf(__('%1$s Module Disabled', 'prorank-seo'), $module->get_name()),
                        'message' => sprintf(
/* translators: %1$: value */
                            __('The %1$s module is currently disabled. Enable it to improve your SEO.', 'prorank-seo'),
                            $module->get_name()
                        ),
                        'action' => [
                            'label' => __('Enable Module', 'prorank-seo'),
                            'url' => admin_url('admin.php?page=prorank-seo'),
                        ],
                        'dismissible' => true,
                        'timestamp' => $current_time,
                    ];
                }
            }
        }

        return $notifications;
    }

    /**
     * Get remote notifications from API
     *
     * @return array
     */
    private function get_remote_notifications(): array {
        // Check cache first
        $cached = get_transient($this->cache_key);
        if ($cached !== false) {
            $cached = array_map(function ($notification) {
                return is_array($notification) ? $this->normalize_notification($notification) : [];
            }, (array) $cached);
            return array_values(array_filter($cached));
        }

        // Fetch from API
        $response = wp_remote_get($this->getFeedUrl(), [
            'timeout' => 10,
            'headers' => [
                'X-ProRank-Version' => defined('PRORANK_SEO_VERSION') ? PRORANK_SEO_VERSION : '0.1.0',
                'X-ProRank-License' => $this->license_manager->get_license_key() ?: 'free',
                'X-ProRank-Tier' => $this->license_manager->get_tier(),
            ],
        ]);

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

        $body = wp_remote_retrieve_body($response);
        $data = json_decode($body, true);

        if (!is_array($data) || empty($data['notifications'])) {
            return [];
        }

        $notifications = array_map(function ($notification) {
            return is_array($notification) ? $this->normalize_notification($notification) : [];
        }, $data['notifications']);
        $notifications = array_values(array_filter($notifications));

        // Filter by tier
        $current_tier = $this->license_manager->get_tier();
        $notifications = array_filter($notifications, function($notification) use ($current_tier) {
            if (!isset($notification['tiers'])) {
                return true; // Show to all if no tier restriction
            }
            return in_array($current_tier, $notification['tiers'], true);
        });

        // Cache for 6 hours
        set_transient($this->cache_key, $notifications, 6 * HOUR_IN_SECONDS);

        return $notifications;
    }

    /**
     * Dismiss a notification
     *
     * @param string $notification_id Notification ID
     * @return bool
     */
    public function dismiss_notification(string $notification_id): bool {
        $dismissed = $this->get_dismissed_notifications();
        
        if (!in_array($notification_id, $dismissed, true)) {
            $dismissed[] = $notification_id;
            return update_user_meta(get_current_user_id(), 'prorank_dismissed_notifications', $dismissed);
        }

        return true;
    }

    /**
     * Get dismissed notifications for current user
     *
     * @return array
     */
    private function get_dismissed_notifications(): array {
        $dismissed = get_user_meta(get_current_user_id(), 'prorank_dismissed_notifications', true);
        return is_array($dismissed) ? $dismissed : [];
    }

    /**
     * Clear notifications cache
     *
     * @return void
     */
    public function clear_cache(): void {
        delete_transient($this->cache_key);
    }

    /**
     * Get notification counts by type
     *
     * @return array
     */
    public function get_notification_counts(): array {
        $notifications = $this->get_notifications();
        $counts = [
            'total' => count($notifications),
            'by_type' => [],
            'by_priority' => [],
        ];

        foreach ($notifications as $notification) {
            $type = $notification['type'] ?? 'info';
            $priority = $notification['priority'] ?? 'low';

            if (!isset($counts['by_type'][$type])) {
                $counts['by_type'][$type] = 0;
            }
            $counts['by_type'][$type]++;

            if (!isset($counts['by_priority'][$priority])) {
                $counts['by_priority'][$priority] = 0;
            }
            $counts['by_priority'][$priority]++;
        }

        return $counts;
    }
}
