<?php

declare(strict_types=1);

namespace WPUltimateSecurity\UltimateSecurity\Modules\Authentication;

use WPUltimateSecurity\UltimateSecurity\Modules\Base\AbstractModule;
use WPUltimateSecurity\UltimateSecurity\Support\Helpers\Utils;
use WPUltimateSecurity\UltimateSecurity\Support\Helpers\IpHelper;
use WPUltimateSecurity\UltimateSecurity\Modules\Authentication\LoginNotifications\EmailNotifier;
use WPUltimateSecurity\UltimateSecurity\Modules\Authentication\LoginNotifications\DigestService;

/**
 * Login Notifications Module
 *
 * Handles email notifications for login-related security events including:
 * - Failed login attempts (threshold-based)
 * - Account lockouts
 * - New IP/device logins
 * - Successful login after failed attempts
 * - Daily digest summaries
 * - Weekly security reports
 */
class LoginNotifications extends AbstractModule
{
    public const MODULE_ID = 'auth.login_notifications';

    protected const VERSION = '2.0.0';

    /**
     * Get unique module identifier.
     */
    public static function getId(): string
    {
        return self::MODULE_ID;
    }

    /**
     * Get human-readable module name.
     */
    public static function getName(): string
    {
        return __('Login Notifications', 'ultimate-security');
    }

    /**
     * Get module domain.
     */
    public static function getDomain(): string
    {
        return 'authentication';
    }
    /**
     * @var array Plugin options
     */
    protected static $options;

    /**
     * @var array Login notifications specific options
     */
    protected static $notification_options;

    /**
     * Transient key for tracking failed attempts per IP
     */
    protected const FAILED_ATTEMPTS_TRANSIENT = 'ultimate_security_failed_attempts_';

    /**
     * Transient key for tracking notification rate limiting
     */
    protected const NOTIFICATION_RATE_TRANSIENT = 'ultimate_security_notification_rate';

    /**
     * User meta key for known IPs
     */
    protected const KNOWN_IPS_META = 'ultimate_security_known_ips';

    /**
     * User meta key for known devices
     */
    protected const KNOWN_DEVICES_META = 'ultimate_security_known_devices';

    /**
     * User meta key for recent failed attempts count
     */
    protected const RECENT_FAILS_META = 'ultimate_security_recent_failed_attempts';

    /**
     * Option key for notification logs
     */
    protected const NOTIFICATION_LOG_OPTION = 'ultimate_security_notification_log';

    /**
     * Cron hooks
     */
    protected const DAILY_DIGEST_HOOK = 'ultimate_security_daily_login_digest';
    protected const WEEKLY_SUMMARY_HOOK = 'ultimate_security_weekly_login_summary';

    /**
     * Boot the module after registration.
     */
    public static function boot(): void
    {
        $options = Utils::ultimate_security_get_option();
        self::$options = $options;
        self::$notification_options = $options['security']['login_notifications'] ?? self::get_default_options();

        // Only initialize if enabled
        if (self::$notification_options['enabled'] !== '1') {
            self::unschedule_cron_events();
            return;
        }

        self::init();
    }

    /**
     * Get default options
     */
    protected static function get_default_options(): array
    {
        return [
            'enabled' => '1',
            'notify_on_failed' => '1',
            'failed_threshold' => '3',
            'notify_on_lockout' => '1',
            'notify_on_new_ip' => '0',
            'notify_on_new_device' => '0',
            'notify_on_success_after_fail' => '1',
            'daily_digest' => '1',
            'daily_digest_time' => '09:00',
            'weekly_summary' => '1',
            'weekly_summary_day' => 'monday',
            'notification_email' => '',
            'include_geolocation' => '1',
            'one_click_block' => '1',
        ];
    }

    /**
     * Initialize hooks and cron jobs
     */
    protected static function init()
    {
        // Login event hooks
        add_action('wp_login_failed', [self::class, 'on_login_failed'], 20);
        add_action('wp_login', [self::class, 'on_login_success'], 20, 2);

        // Hook into existing lockout system
        add_action('ultimate_security_user_locked', [self::class, 'on_user_locked'], 10, 2);
        add_action('ultimate_security_log_event', [self::class, 'on_security_event'], 10, 2);

        // Cron schedules
        add_filter('cron_schedules', [self::class, 'add_cron_schedules']);

        // Schedule cron events
        self::schedule_cron_events();

        // Cron handlers
        add_action(self::DAILY_DIGEST_HOOK, [self::class, 'send_daily_digest']);
        add_action(self::WEEKLY_SUMMARY_HOOK, [self::class, 'send_weekly_summary']);

        // Admin AJAX for one-click block
        add_action('wp_ajax_ultimate_security_block_ip', [self::class, 'ajax_block_ip']);
    }

    /**
     * Add custom cron schedules
     */
    public static function add_cron_schedules(array $schedules): array
    {
        $schedules['ultimate_security_daily'] = [
            'interval' => DAY_IN_SECONDS,
            'display' => __('Once Daily', 'ultimate-security'),
        ];

        $schedules['ultimate_security_weekly'] = [
            'interval' => WEEK_IN_SECONDS,
            'display' => __('Once Weekly', 'ultimate-security'),
        ];

        return $schedules;
    }

    /**
     * Schedule cron events for digests
     */
    protected static function schedule_cron_events()
    {
        // Daily digest
        if (self::$notification_options['daily_digest'] === '1') {
            if (!wp_next_scheduled(self::DAILY_DIGEST_HOOK)) {
                $time = self::get_next_scheduled_time(self::$notification_options['daily_digest_time']);
                wp_schedule_event($time, 'ultimate_security_daily', self::DAILY_DIGEST_HOOK);
            }
        } else {
            wp_clear_scheduled_hook(self::DAILY_DIGEST_HOOK);
        }

        // Weekly summary
        if (self::$notification_options['weekly_summary'] === '1') {
            if (!wp_next_scheduled(self::WEEKLY_SUMMARY_HOOK)) {
                $day = self::$notification_options['weekly_summary_day'];
                $time = self::get_next_weekly_time($day);
                wp_schedule_event($time, 'ultimate_security_weekly', self::WEEKLY_SUMMARY_HOOK);
            }
        } else {
            wp_clear_scheduled_hook(self::WEEKLY_SUMMARY_HOOK);
        }
    }

    /**
     * Unschedule cron events
     */
    protected static function unschedule_cron_events()
    {
        wp_clear_scheduled_hook(self::DAILY_DIGEST_HOOK);
        wp_clear_scheduled_hook(self::WEEKLY_SUMMARY_HOOK);
    }

    /**
     * Get next scheduled time for daily digest
     */
    protected static function get_next_scheduled_time(string $time): int
    {
        $timezone = wp_timezone();
        $now = new \DateTime('now', $timezone);
        $scheduled = new \DateTime($time, $timezone);

        if ($scheduled <= $now) {
            $scheduled->modify('+1 day');
        }

        return $scheduled->getTimestamp();
    }

    /**
     * Get next weekly scheduled time
     */
    protected static function get_next_weekly_time(string $day): int
    {
        $timezone = wp_timezone();
        $now = new \DateTime('now', $timezone);
        $scheduled = new \DateTime("next {$day} 09:00", $timezone);

        return $scheduled->getTimestamp();
    }

    /**
     * Handle failed login attempt
     */
    public static function on_login_failed(string $username)
    {
        if (self::$notification_options['notify_on_failed'] !== '1') {
            return;
        }

        $ip = IpHelper::getClientIp();
        $threshold = (int) self::$notification_options['failed_threshold'];

        // Track failed attempts for this IP
        $transient_key = self::FAILED_ATTEMPTS_TRANSIENT . md5($ip);
        $attempts = (int) get_transient($transient_key);
        $attempts++;
        set_transient($transient_key, $attempts, HOUR_IN_SECONDS);

        // Log the event for digest
        self::log_event('failed_login', [
            'username' => $username,
            'ip' => $ip,
            'user_agent' => self::get_user_agent(),
            'timestamp' => time(),
        ]);

        // Track per-user recent fails
        $user = get_user_by('login', $username);
        if ($user) {
            $recent_fails = (int) get_user_meta($user->ID, self::RECENT_FAILS_META, true);
            update_user_meta($user->ID, self::RECENT_FAILS_META, $recent_fails + 1);
        }

        // Send notification if threshold reached
        if ($attempts >= $threshold) {
            self::send_failed_attempts_notification($ip, $username, $attempts);
            // Reset counter after notification
            delete_transient($transient_key);
        }
    }

    /**
     * Handle successful login
     */
    public static function on_login_success(string $user_login, \WP_User $user)
    {
        $ip = IpHelper::getClientIp();
        $user_agent = self::get_user_agent();

        // Check for new IP
        if (self::$notification_options['notify_on_new_ip'] === '1') {
            if (self::is_new_ip($user->ID, $ip)) {
                self::send_new_ip_notification($user, $ip, $user_agent);
                self::add_known_ip($user->ID, $ip);
            }
        }

        // Check for new device
        if (self::$notification_options['notify_on_new_device'] === '1') {
            $device_hash = self::get_device_hash($user_agent);
            if (self::is_new_device($user->ID, $device_hash)) {
                self::send_new_device_notification($user, $ip, $user_agent);
                self::add_known_device($user->ID, $device_hash);
            }
        }

        // Check if login after failed attempts
        if (self::$notification_options['notify_on_success_after_fail'] === '1') {
            $recent_fails = (int) get_user_meta($user->ID, self::RECENT_FAILS_META, true);
            if ($recent_fails > 0) {
                self::send_success_after_fail_notification($user, $ip, $recent_fails);
                delete_user_meta($user->ID, self::RECENT_FAILS_META);
            }
        }

        // Log successful login for digest
        self::log_event('successful_login', [
            'user_id' => $user->ID,
            'username' => $user_login,
            'ip' => $ip,
            'user_agent' => $user_agent,
            'timestamp' => time(),
        ]);
    }

    /**
     * Handle user lockout event
     */
    public static function on_user_locked(int $user_id, array $data = [])
    {
        if (self::$notification_options['notify_on_lockout'] !== '1') {
            return;
        }

        $user = get_user_by('ID', $user_id);
        if (!$user) {
            return;
        }

        $ip = IpHelper::getClientIp();
        $user_agent = self::get_user_agent();

        self::log_event('lockout', [
            'user_id' => $user_id,
            'username' => $user->user_login,
            'ip' => $ip,
            'user_agent' => $user_agent,
            'timestamp' => time(),
            'duration' => $data['duration_m'] ?? 0,
            'phase' => $data['phase'] ?? 1,
        ]);

        EmailNotifier::send_lockout_notification($user, $ip, $user_agent, $data);
    }

    /**
     * Handle security events from other modules
     */
    public static function on_security_event(string $event_type, array $data)
    {
        if ($event_type === 'user_lockout' && self::$notification_options['notify_on_lockout'] === '1') {
            self::on_user_locked($data['user_id'] ?? 0, $data);
        }
    }

    /**
     * Send notification for failed login attempts threshold
     */
    protected static function send_failed_attempts_notification(string $ip, string $username, int $attempts)
    {
        if (!self::can_send_notification()) {
            return;
        }

        $user_agent = self::get_user_agent();
        $geolocation = self::$notification_options['include_geolocation'] === '1'
            ? self::get_geolocation($ip)
            : null;

        EmailNotifier::send_failed_attempts_notification([
            'ip' => $ip,
            'username' => $username,
            'attempts' => $attempts,
            'user_agent' => $user_agent,
            'geolocation' => $geolocation,
            'timestamp' => time(),
        ]);

        self::increment_notification_count();
    }

    /**
     * Send notification for new IP login
     */
    protected static function send_new_ip_notification(\WP_User $user, string $ip, string $user_agent)
    {
        if (!self::can_send_notification()) {
            return;
        }

        $geolocation = self::$notification_options['include_geolocation'] === '1'
            ? self::get_geolocation($ip)
            : null;

        EmailNotifier::send_new_ip_notification($user, $ip, $user_agent, $geolocation);
        self::increment_notification_count();
    }

    /**
     * Send notification for new device login
     */
    protected static function send_new_device_notification(\WP_User $user, string $ip, string $user_agent)
    {
        if (!self::can_send_notification()) {
            return;
        }

        $geolocation = self::$notification_options['include_geolocation'] === '1'
            ? self::get_geolocation($ip)
            : null;

        $parsed_ua = Utils::parse_user_agent($user_agent);

        EmailNotifier::send_new_device_notification($user, $ip, $user_agent, $parsed_ua, $geolocation);
        self::increment_notification_count();
    }

    /**
     * Send notification for successful login after failed attempts
     */
    protected static function send_success_after_fail_notification(\WP_User $user, string $ip, int $failed_attempts)
    {
        if (!self::can_send_notification()) {
            return;
        }

        $user_agent = self::get_user_agent();
        $geolocation = self::$notification_options['include_geolocation'] === '1'
            ? self::get_geolocation($ip)
            : null;

        EmailNotifier::send_success_after_fail_notification($user, $ip, $user_agent, $failed_attempts, $geolocation);
        self::increment_notification_count();
    }

    /**
     * Send daily digest email
     */
    public static function send_daily_digest()
    {
        $events = self::get_events_since(strtotime('-24 hours'));

        if (empty($events)) {
            return; // No events to report
        }

        $stats = DigestService::compile_daily_stats($events);
        EmailNotifier::send_daily_digest($stats);
    }

    /**
     * Send weekly summary email
     */
    public static function send_weekly_summary()
    {
        $events = self::get_events_since(strtotime('-7 days'));

        if (empty($events)) {
            return; // No events to report
        }

        $stats = DigestService::compile_weekly_stats($events);
        EmailNotifier::send_weekly_summary($stats);

        // Clean up old logs
        self::cleanup_old_logs();
    }

    /**
     * Log an event for digest compilation
     */
    protected static function log_event(string $type, array $data)
    {
        $logs = get_option(self::NOTIFICATION_LOG_OPTION, []);

        $logs[] = [
            'type' => $type,
            'data' => $data,
            'timestamp' => time(),
        ];

        // Keep only last 7 days of logs
        $cutoff = strtotime('-7 days');
        $logs = array_filter($logs, function ($log) use ($cutoff) {
            return $log['timestamp'] >= $cutoff;
        });

        update_option(self::NOTIFICATION_LOG_OPTION, array_values($logs), false);
    }

    /**
     * Get events since a timestamp
     */
    public static function get_events_since(int $timestamp): array
    {
        $logs = get_option(self::NOTIFICATION_LOG_OPTION, []);

        return array_filter($logs, function ($log) use ($timestamp) {
            return $log['timestamp'] >= $timestamp;
        });
    }

    /**
     * Get recent notifications for the admin panel
     */
    public static function get_recent_notifications(int $limit = 50): array
    {
        $logs = get_option(self::NOTIFICATION_LOG_OPTION, []);

        // Sort by timestamp descending
        usort($logs, function ($a, $b) {
            return $b['timestamp'] - $a['timestamp'];
        });

        return array_slice($logs, 0, $limit);
    }

    /**
     * Cleanup old logs
     */
    protected static function cleanup_old_logs()
    {
        $logs = get_option(self::NOTIFICATION_LOG_OPTION, []);
        $cutoff = strtotime('-30 days');

        $logs = array_filter($logs, function ($log) use ($cutoff) {
            return $log['timestamp'] >= $cutoff;
        });

        update_option(self::NOTIFICATION_LOG_OPTION, array_values($logs), false);
    }

    /**
     * Check if this is a new IP for the user
     */
    protected static function is_new_ip(int $user_id, string $ip): bool
    {
        $known_ips = get_user_meta($user_id, self::KNOWN_IPS_META, true);

        if (!is_array($known_ips)) {
            $known_ips = [];
        }

        return !in_array($ip, $known_ips, true);
    }

    /**
     * Add IP to user's known IPs
     */
    protected static function add_known_ip(int $user_id, string $ip)
    {
        $known_ips = get_user_meta($user_id, self::KNOWN_IPS_META, true);

        if (!is_array($known_ips)) {
            $known_ips = [];
        }

        $known_ips[] = $ip;

        // Keep only the last 50 IPs
        $known_ips = array_slice(array_unique($known_ips), -50);

        update_user_meta($user_id, self::KNOWN_IPS_META, $known_ips);
    }

    /**
     * Check if this is a new device for the user
     */
    protected static function is_new_device(int $user_id, string $device_hash): bool
    {
        $known_devices = get_user_meta($user_id, self::KNOWN_DEVICES_META, true);

        if (!is_array($known_devices)) {
            $known_devices = [];
        }

        return !in_array($device_hash, $known_devices, true);
    }

    /**
     * Add device to user's known devices
     */
    protected static function add_known_device(int $user_id, string $device_hash)
    {
        $known_devices = get_user_meta($user_id, self::KNOWN_DEVICES_META, true);

        if (!is_array($known_devices)) {
            $known_devices = [];
        }

        $known_devices[] = $device_hash;

        // Keep only the last 20 devices
        $known_devices = array_slice(array_unique($known_devices), -20);

        update_user_meta($user_id, self::KNOWN_DEVICES_META, $known_devices);
    }

    /**
     * Get hash for device identification
     */
    protected static function get_device_hash(string $user_agent): string
    {
        return md5($user_agent);
    }

    /**
     * Get current user agent
     */
    protected static function get_user_agent(): string
    {
        return isset($_SERVER['HTTP_USER_AGENT'])
            ? sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT']))
            : '';
    }

    /**
     * Get geolocation data for an IP
     */
    public static function get_geolocation(string $ip): ?array
    {
        // Check for local/private IPs
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false) {
            return [
                'country' => __('Local Network', 'ultimate-security'),
                'city' => '',
                'region' => '',
            ];
        }

        // Use free IP-API for geolocation
        // REMOVED: MaxMind integration was part of restrictions (server-level features)
        return self::get_ipapi_geolocation($ip);
    }

    /**
     * Get geolocation from free IP-API service
     */
    protected static function get_ipapi_geolocation(string $ip): ?array
    {
        $cache_key = 'ultimate_security_geo_' . md5($ip);
        $cached = get_transient($cache_key);

        if ($cached !== false) {
            return $cached;
        }

        $response = wp_remote_get("http://ip-api.com/json/{$ip}?fields=status,country,regionName,city", [
            'timeout' => 5,
        ]);

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

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

        if (!$body || $body['status'] !== 'success') {
            return null;
        }

        $geo = [
            'country' => $body['country'] ?? '',
            'region' => $body['regionName'] ?? '',
            'city' => $body['city'] ?? '',
        ];

        // Cache for 24 hours
        set_transient($cache_key, $geo, DAY_IN_SECONDS);

        return $geo;
    }

    /**
     * Check if we can send a notification (rate limiting)
     */
    protected static function can_send_notification(): bool
    {
        $rate_data = get_transient(self::NOTIFICATION_RATE_TRANSIENT);

        if ($rate_data === false) {
            return true;
        }

        $max_per_hour = (int) (self::$options['security']['lockout_notifications']['rate_limit'] ?? 10);

        return $rate_data['count'] < $max_per_hour;
    }

    /**
     * Increment notification count for rate limiting
     */
    protected static function increment_notification_count()
    {
        $rate_data = get_transient(self::NOTIFICATION_RATE_TRANSIENT);

        if ($rate_data === false) {
            $rate_data = ['count' => 0, 'started' => time()];
        }

        $rate_data['count']++;

        // Reset if hour has passed
        if (time() - $rate_data['started'] >= HOUR_IN_SECONDS) {
            $rate_data = ['count' => 1, 'started' => time()];
        }

        set_transient(self::NOTIFICATION_RATE_TRANSIENT, $rate_data, HOUR_IN_SECONDS);
    }

    /**
     * Get the notification email address
     */
    public static function get_notification_email(): string
    {
        $email = self::$notification_options['notification_email'] ?? '';

        if (empty($email)) {
            $email = get_option('admin_email');
        }

        return $email;
    }

    /**
     * AJAX handler for one-click IP blocking
     * REMOVED: IP blocking was part of restrictions (server-level features)
     * IP blocking should be done at the server level (Nginx, Apache, Cloudflare)
     */
    public static function ajax_block_ip()
    {
        check_ajax_referer('ultimate_security_block_ip', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => __('Unauthorized', 'ultimate-security')]);
        }

        // Feature removed - IP blocking should be done at server level
        wp_send_json_error([
            'message' => __('IP blocking has been moved to server-level configuration. Please use your server firewall (Nginx, Apache, or Cloudflare) to block IPs.', 'ultimate-security')
        ]);
    }

    /**
     * Send test notification
     */
    public static function send_test_notification(string $email): bool
    {
        return EmailNotifier::send_test_notification($email);
    }
}
