<?php
namespace WPUltimateSecurity\UltimateSecurity\Infrastructure\WordPress\Hooks;

use WPUltimateSecurity\UltimateSecurity\Contracts\HookableInterface;
use WPUltimateSecurity\UltimateSecurity\Support\Helpers\Utils;
use WPUltimateSecurity\UltimateSecurity\Support\Helpers\IpHelper;
use WPUltimateSecurity\UltimateSecurity\Support\Helpers\DateHelper;
use WPUltimateSecurity\UltimateSecurity\Modules\Authentication\TwoFactor\Services\TrustedDeviceService;
use WPUltimateSecurity\UltimateSecurity\Support\Validation\ProFeatureMiddleware;
use WP_User;

class CustomHooks implements HookableInterface
{
    /**
     * Get the list of hooks to register.
     *
     * @return array<int, array{type: string, hook: string, callback: callable, priority?: int, accepted_args?: int}>
     */
    public function getHooks(): array
    {
        return [
            [
                'type' => 'action',
                'hook' => 'init',
                'callback' => [self::class, 'ultimate_security_logout_current_user'],
                'priority' => 10,
                'accepted_args' => 1,
            ],
            [
                'type' => 'action',
                'hook' => 'init',
                'callback' => [self::class, 'ultimate_security_block_lost_password_page'],
                'priority' => 999,
                'accepted_args' => 0,
            ],
            [
                'type' => 'action',
                'hook' => 'admin_init',
                'callback' => [self::class, 'ultimate_security_logout_current_user'],
                'priority' => 10,
                'accepted_args' => 1,
            ],
            [
                'type' => 'action',
                'hook' => 'wp_login',
                'callback' => [self::class, 'ultimate_security_log_successful_login'],
                'priority' => 10,
                'accepted_args' => 2,
            ],
            [
                'type' => 'action',
                'hook' => 'wp_login_failed',
                'callback' => [self::class, 'ultimate_security_log_failed_login'],
                'priority' => 10,
                'accepted_args' => 1,
            ],
            [
                'type' => 'action',
                'hook' => 'admin_head',
                'callback' => [self::class, 'ultimate_security_admin_menu_name_show_in_two_line'],
                'priority' => 10,
                'accepted_args' => 1,
            ],
            [
                'type' => 'action',
                'hook' => 'admin_notices',
                'callback' => [self::class, 'ultimate_security_password_expiry_warning_admin'],
                'priority' => 10,
                'accepted_args' => 1,
            ],
            [
                'type' => 'action',
                'hook' => 'wp_footer',
                'callback' => [self::class, 'ultimate_security_password_expiry_warning_frontend'],
                'priority' => 10,
                'accepted_args' => 1,
            ],
            [
                'type' => 'action',
                'hook' => 'wp_set_password',
                'callback' => [self::class, 'ultimate_security_track_password_change'],
                'priority' => 10,
                'accepted_args' => 2,
            ],
            [
                'type' => 'action',
                'hook' => 'ultimate_security_trusted_devices_cleanup',
                'callback' => [self::class, 'cleanup_trusted_devices'],
                'priority' => 10,
                'accepted_args' => 1,
            ],
        ];
    }

    /**
     * Register all hooks with WordPress (instance method for HookableInterface).
     *
     * @return void
     */
    public function register(): void
    {
        foreach ($this->getHooks() as $hook) {
            // Check condition if present
            if (isset($hook['condition']) && !call_user_func($hook['condition'])) {
                continue;
            }

            $function = $hook['type'] === 'filter' ? 'add_filter' : 'add_action';
            $function(
                $hook['hook'],
                $hook['callback'],
                $hook['priority'] ?? 10,
                $hook['accepted_args'] ?? 1
            );
        }

        // Schedule trusted devices cleanup cron
        self::schedule_trusted_devices_cleanup();
    }

    /**
     * Schedule daily cleanup of expired trusted devices
     */
    private static function schedule_trusted_devices_cleanup()
    {
        if (!wp_next_scheduled('ultimate_security_trusted_devices_cleanup')) {
            wp_schedule_event(time(), 'daily', 'ultimate_security_trusted_devices_cleanup');
        }
    }

    /**
     * Cleanup expired and revoked trusted devices (Pro feature)
     */
    public static function cleanup_trusted_devices()
    {
        // Only run cleanup if Pro is active (trusted devices is a Pro feature)
        if (ProFeatureMiddleware::hasProFeature('trusted_devices') && class_exists(TrustedDeviceService::class)) {
            TrustedDeviceService::cleanup_expired_devices(30);
        }
    }

    public static function ultimate_security_logout_current_user()
    {
        $ultimate_security_options = Utils::ultimate_security_get_option();
        if (is_user_logged_in()) {
            $current_user_id = get_current_user_id();
            $user_password_history = get_user_meta($current_user_id, 'user_password_history', true);

            if (isset($ultimate_security_options['security']['password_policies']['change_initial_password']) && !empty($ultimate_security_options['security']['password_policies']['change_initial_password'])) {
                $ultimate_security_installed = get_option('ultimate_security_installed');
                $user_by_id = get_user_by('id', $current_user_id);
                $user_registered = $user_by_id->data->user_registered;
                $date1 = date_create($ultimate_security_installed);
                $date2 = date_create($user_registered);
                $diff = date_diff($date1, $date2);
                if ($diff->format("%R") == '+' && !is_array($user_password_history)) {
                    $user_object = new WP_User($current_user_id);
                    $key = get_password_reset_key($user_object);
                    $reset_password_url = network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_object->user_login), 'login');
                    wp_logout();
                    wp_redirect($reset_password_url);
                    exit;
                }
            }


            if (is_array($user_password_history) && sizeof($user_password_history)) {
                if (isset($ultimate_security_options['security']['password_policies']['password_expiry_value']) && (int) $ultimate_security_options['security']['password_policies']['password_expiry_value'] > 0) {
                    $unit = (isset($ultimate_security_options['security']['password_policies']['password_expiry_unit']) && $ultimate_security_options['security']['password_policies']['password_expiry_unit']) ? $ultimate_security_options['security']['password_policies']['password_expiry_unit'] : 'month';
                    $grace_days = isset($ultimate_security_options['security']['password_policies']['password_expiry_grace_days']) ? (int) $ultimate_security_options['security']['password_policies']['password_expiry_grace_days'] : 3;

                    $lass_pass_date = array_key_first($user_password_history);
                    $date1 = date_create($lass_pass_date);
                    $date2 = date_create(DateHelper::now());
                    $diff = date_diff($date1, $date2);
                    $time_passed = 0;
                    if ($unit == 'year') {
                        $time_passed = $diff->format('%y');
                    } else {
                        $time_passed = $diff->format('%y') * 12 + $diff->format('%m');
                    }

                    $expiry_value = (int) $ultimate_security_options['security']['password_policies']['password_expiry_value'];

                    if ($time_passed >= $expiry_value) {
                        // Password has expired - calculate days past expiration
                        $days_since_expiry = self::calculate_days_since_expiry($lass_pass_date, $expiry_value, $unit);

                        if ($grace_days > 0 && $days_since_expiry <= $grace_days) {
                            // In grace period - allow login but force to change password page
                            // Only redirect if not already on password reset or profile page
                            $current_url = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'])) : '';
                            $allowed_pages = ['wp-login.php', 'profile.php', 'user-edit.php'];
                            $is_allowed_page = false;
                            foreach ($allowed_pages as $page) {
                                if (strpos($current_url, $page) !== false) {
                                    $is_allowed_page = true;
                                    break;
                                }
                            }

                            if (!$is_allowed_page && is_admin()) {
                                // Redirect to profile page in admin
                                wp_redirect(admin_url('profile.php?password_expired=1'));
                                exit;
                            }
                        } else {
                            // Grace period exceeded or no grace period - force logout
                            $user_object = new WP_User($current_user_id);
                            $key = get_password_reset_key($user_object);
                            $reset_password_url = network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_object->user_login), 'login');
                            wp_logout();
                            wp_redirect($reset_password_url);
                            exit;
                        }
                    }
                }
            }
        }

        if (isset($ultimate_security_options['security']['password_policies']['disable_self_reset']) && $ultimate_security_options['security']['password_policies']['disable_self_reset'] === '1') {
            // Disable password reset notification to admin
            add_action('wp_password_change_notification', '__return_false');
            // Disable password reset email to user
            add_filter('retrieve_password_message', '__return_false');
        }
    }

    /**
     * Block access to lost password page when self-service reset is disabled
     */
    public static function ultimate_security_block_lost_password_page()
    {
        // Skip if doing AJAX or cron
        if (wp_doing_ajax() || wp_doing_cron()) {
            return;
        }

        // Skip if already redirecting
        if (headers_sent()) {
            return;
        }

        $ultimate_security_options = Utils::ultimate_security_get_option();

        // Check if self-service reset is disabled
        $disable_self_reset = isset($ultimate_security_options['security']['password_policies']['disable_self_reset'])
            && $ultimate_security_options['security']['password_policies']['disable_self_reset'] === '1';

        if (!$disable_self_reset) {
            return;
        }

        // Check if we're on lost password page
        global $pagenow;
        $action = isset($_GET['action']) ? sanitize_text_field(wp_unslash($_GET['action'])) : '';

        $is_lost_password_page = (
            $pagenow === 'wp-login.php'
            && $action === 'lostpassword'
        );

        // Also check REQUEST_URI for cases with custom login URL
        if (!$is_lost_password_page) {
            $request_uri = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'])) : '';
            $is_lost_password_page = strpos($request_uri, 'action=lostpassword') !== false;
        }

        if ($is_lost_password_page) {
            // Redirect to login page
            $redirect_url = wp_login_url();
            wp_safe_redirect($redirect_url);
            exit;
        }
    }

    public static function ultimate_security_log_successful_login($user_login, $user)
    {
        self::ultimate_security_store_login_log($user_login, true);
    }
    public static function ultimate_security_log_failed_login($username)
    {
        if (!empty($username)) {
            $user_data = get_user_by('login', $username);

            if ($user_data) {
                // Valid username, wrong password
                self::ultimate_security_store_login_log($username, false);
            } else {
                // Invalid username
                self::ultimate_security_store_login_log($username, false);
            }
        }
    }


    public static function ultimate_security_store_login_log($username, $success)
    {
        $logs = get_option('ultimate_security_login_logs', []);
        if (!is_array($logs)) {
            $logs = [];
        }

        $ip = IpHelper::getClientIp();
        $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])) : '';

        $logs[] = [
            'username' => $username,
            'success' => $success,
            'timestamp' => DateHelper::timestamp(),
            'ip' => $ip,
            'user_agent' => $user_agent,
        ];


        update_option('ultimate_security_login_logs', $logs);
    }
    public static function ultimate_security_admin_menu_name_show_in_two_line()
    {
        ?>
        <style>
            #adminmenu .toplevel_page_ultimate-security .wp-menu-name {
                white-space: normal;
                line-height: 1.2;
            }
        </style>
        <script>
            jQuery(document).ready(function ($) {
                const item = $('#adminmenu .toplevel_page_ultimate-security .wp-menu-name');
                const content = item.html();

                // Replace only the first space with <br>
                const newContent = content.replace(' ', '<br>');
                item.html(newContent);
            });
        </script>
        <?php
    }

    /**
     * Track password change date
     */
    public static function ultimate_security_track_password_change($password, $user_id)
    {
        update_user_meta($user_id, 'ultimate_security_password_changed_at', DateHelper::now());
    }

    /**
     * Calculate days since password expired
     */
    private static function calculate_days_since_expiry($last_pass_date, $expiry_value, $unit)
    {
        $last_change = new \DateTime($last_pass_date);

        // Add expiry period to get expiration date
        if ($unit === 'year') {
            $last_change->modify('+' . $expiry_value . ' years');
        } else {
            $last_change->modify('+' . $expiry_value . ' months');
        }

        $now = new \DateTime(DateHelper::now());
        $diff = $now->diff($last_change);

        // If now is after expiration date, return positive days
        if ($now > $last_change) {
            return $diff->days;
        }

        return 0;
    }

    /**
     * Calculate days until password expires
     */
    private static function calculate_days_until_expiry($last_pass_date, $expiry_value, $unit)
    {
        $last_change = new \DateTime($last_pass_date);

        // Add expiry period to get expiration date
        if ($unit === 'year') {
            $last_change->modify('+' . $expiry_value . ' years');
        } else {
            $last_change->modify('+' . $expiry_value . ' months');
        }

        $now = new \DateTime(DateHelper::now());
        $diff = $last_change->diff($now);

        // If expiration date is after now, return positive days
        if ($last_change > $now) {
            return $diff->days;
        }

        return 0; // Already expired
    }

    /**
     * Show password expiry warning in admin
     */
    public static function ultimate_security_password_expiry_warning_admin()
    {
        if (!is_user_logged_in()) {
            return;
        }

        $warning_info = self::get_password_expiry_warning_info();
        if (!$warning_info) {
            return;
        }

        $message = '';
        $class = 'notice-warning';

        if ($warning_info['expired']) {
            $class = 'notice-error';
            if ($warning_info['grace_remaining'] > 0) {
                $message = sprintf(
                    /* translators: %d: number of days */
                    __('Your password has expired. You have %d day(s) remaining to change it before being locked out.', 'ultimate-security'),
                    $warning_info['grace_remaining']
                );
            } else {
                $message = __('Your password has expired. Please change it immediately.', 'ultimate-security');
            }
        } else {
            $message = sprintf(
                /* translators: %d: number of days */
                __('Your password will expire in %d day(s). Please change it soon.', 'ultimate-security'),
                $warning_info['days_remaining']
            );
        }

        $profile_url = admin_url('profile.php#password');
        ?>
        <div class="notice <?php echo esc_attr($class); ?> is-dismissible">
            <p>
                <strong><?php echo esc_html__('Password Expiration', 'ultimate-security'); ?>:</strong>
                <?php echo esc_html($message); ?>
                <a href="<?php echo esc_url($profile_url); ?>" class="button button-primary" style="margin-left: 10px;">
                    <?php echo esc_html__('Change Password', 'ultimate-security'); ?>
                </a>
            </p>
        </div>
        <?php
    }

    /**
     * Show password expiry warning on frontend
     */
    public static function ultimate_security_password_expiry_warning_frontend()
    {
        if (!is_user_logged_in() || is_admin()) {
            return;
        }

        $warning_info = self::get_password_expiry_warning_info();
        if (!$warning_info) {
            return;
        }

        $message = '';
        $bg_color = '#fff3cd';
        $border_color = '#ffc107';

        if ($warning_info['expired']) {
            $bg_color = '#f8d7da';
            $border_color = '#dc3545';
            if ($warning_info['grace_remaining'] > 0) {
                $message = sprintf(
                    /* translators: %d: number of days */
                    __('Your password has expired. You have %d day(s) to change it.', 'ultimate-security'),
                    $warning_info['grace_remaining']
                );
            } else {
                $message = __('Your password has expired. Please change it immediately.', 'ultimate-security');
            }
        } else {
            $message = sprintf(
                /* translators: %d: number of days */
                __('Your password will expire in %d day(s).', 'ultimate-security'),
                $warning_info['days_remaining']
            );
        }

        // Try to get WooCommerce account URL, fallback to profile
        $change_url = function_exists('wc_get_account_endpoint_url')
            ? wc_get_account_endpoint_url('edit-account')
            : admin_url('profile.php');
        ?>
        <div id="us-password-expiry-banner" style="position: fixed; bottom: 0; left: 0; right: 0; padding: 12px 20px; background: <?php echo esc_attr($bg_color); ?>; border-top: 3px solid <?php echo esc_attr($border_color); ?>; z-index: 999999; display: flex; justify-content: center; align-items: center; gap: 15px; font-size: 14px;">
            <span><?php echo esc_html($message); ?></span>
            <a href="<?php echo esc_url($change_url); ?>" style="background: #333; color: #fff; padding: 8px 16px; text-decoration: none; border-radius: 4px;">
                <?php echo esc_html__('Change Password', 'ultimate-security'); ?>
            </a>
            <button type="button" onclick="this.parentElement.style.display='none'" style="background: none; border: none; font-size: 18px; cursor: pointer; padding: 0 5px;">&times;</button>
        </div>
        <?php
    }

    /**
     * Get password expiry warning info for current user
     * Returns null if no warning needed, or array with warning details
     */
    private static function get_password_expiry_warning_info()
    {
        $user_id = get_current_user_id();
        if (!$user_id) {
            return null;
        }

        $options = Utils::ultimate_security_get_option();
        $policies = isset($options['security']['password_policies']) ? $options['security']['password_policies'] : [];

        // Check if expiration is enabled
        $expiry_value = isset($policies['password_expiry_value']) ? (int) $policies['password_expiry_value'] : 0;
        if ($expiry_value <= 0) {
            return null;
        }

        $unit = isset($policies['password_expiry_unit']) ? $policies['password_expiry_unit'] : 'month';
        $warning_days = isset($policies['password_expiry_warning_days']) ? (int) $policies['password_expiry_warning_days'] : 7;
        $grace_days = isset($policies['password_expiry_grace_days']) ? (int) $policies['password_expiry_grace_days'] : 3;

        // Get last password change date
        $user_password_history = get_user_meta($user_id, 'user_password_history', true);
        if (!is_array($user_password_history) || empty($user_password_history)) {
            return null;
        }

        $last_pass_date = array_key_first($user_password_history);
        $days_until_expiry = self::calculate_days_until_expiry($last_pass_date, $expiry_value, $unit);
        $days_since_expiry = self::calculate_days_since_expiry($last_pass_date, $expiry_value, $unit);

        // Already expired
        if ($days_since_expiry > 0) {
            return [
                'expired' => true,
                'days_remaining' => 0,
                'grace_remaining' => max(0, $grace_days - $days_since_expiry),
            ];
        }

        // Not yet in warning period
        if ($days_until_expiry > $warning_days) {
            return null;
        }

        // In warning period
        return [
            'expired' => false,
            'days_remaining' => $days_until_expiry,
            'grace_remaining' => $grace_days,
        ];
    }
}
