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

class VMO_Inbound_Email_Sync {
    private static $instance = null;
    private static $hooks_registered = false;

    public static function get_instance() {
        if (null === self::$instance) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    public function init() {
        if (self::$hooks_registered) {
            return;
        }
        self::$hooks_registered = true;
        add_action('vmo_sync_inbound_mailboxes', array($this, 'sync_all_active_mailboxes'));
        add_action('wp_ajax_vmo_sync_inbound_mailboxes', array($this, 'ajax_sync_inbound_mailboxes'));

        if (!wp_next_scheduled('vmo_sync_inbound_mailboxes')) {
            wp_schedule_event(time() + 300, 'hourly', 'vmo_sync_inbound_mailboxes');
        }
    }

    public function ajax_sync_inbound_mailboxes() {
        if (!$this->verify_ajax_request()) {
            wp_send_json_error(array('message' => 'Security check failed'), 403);
        }
        if (!current_user_can('read')) {
            wp_send_json_error(array('message' => 'Unauthorized'), 403);
        }

        $result = $this->sync_all_active_mailboxes(get_current_user_id());
        wp_send_json_success($result);
    }

    private function verify_ajax_request() {
        if (check_ajax_referer('vmo_nonce', 'nonce', false)) {
            return true;
        }
        if (check_ajax_referer('vmo_builder_nonce', 'security', false)) {
            return true;
        }
        $rest_nonce = isset($_SERVER['HTTP_X_WP_NONCE']) ? sanitize_text_field((string) $_SERVER['HTTP_X_WP_NONCE']) : '';
        return $rest_nonce && wp_verify_nonce($rest_nonce, 'wp_rest');
    }

    public function sync_all_active_mailboxes($user_id = null) {
        global $wpdb;

        if (function_exists('vmo_ensure_email_accounts_table')) {
            vmo_ensure_email_accounts_table();
        }

        $accounts_table = $wpdb->prefix . 'vmo_email_accounts';
        if ($wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $accounts_table)) !== $accounts_table) {
            return array('synced' => 0, 'stored' => 0, 'errors' => array('Email accounts table is not available.'));
        }

        if (!function_exists('imap_open')) {
            $message = 'PHP IMAP extension is not available on this server.';
            $where = $user_id ? $wpdb->prepare('WHERE user_id = %d', intval($user_id)) : '';
            // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
            $wpdb->query("UPDATE {$accounts_table} SET last_sync_error = '" . esc_sql($message) . "', last_synced_at = '" . esc_sql(current_time('mysql')) . "' {$where}");
            return array('synced' => 0, 'stored' => 0, 'errors' => array($message));
        }

        $where_user = $user_id ? $wpdb->prepare('AND user_id = %d', intval($user_id)) : '';
        $accounts = $wpdb->get_results(
            "SELECT id, user_id, email, email_address, imap_host, imap_port, encrypted_password, last_sync_uid, last_synced_at
             FROM {$accounts_table}
             WHERE status = 'active'
               AND verified = 1
               AND imap_host <> ''
               AND encrypted_password IS NOT NULL
               AND encrypted_password <> ''
               {$where_user}
             ORDER BY last_synced_at ASC, id ASC
             LIMIT 25",
            ARRAY_A
        );

        $summary = array('synced' => 0, 'stored' => 0, 'errors' => array());
        foreach (($accounts ?: array()) as $account) {
            $result = $this->sync_single_mailbox($account);
            $summary['synced']++;
            $summary['stored'] += intval($result['stored'] ?? 0);
            if (!empty($result['error'])) {
                $summary['errors'][] = sanitize_text_field($result['email'] . ': ' . $result['error']);
            }
        }

        return $summary;
    }

    private function sync_single_mailbox(array $account) {
        global $wpdb;

        $accounts_table = $wpdb->prefix . 'vmo_email_accounts';
        $email = sanitize_email((string) ($account['email'] ?: ($account['email_address'] ?? '')));
        $mailbox_id = intval($account['id'] ?? 0);
        $user_id = intval($account['user_id'] ?? 0);
        $password = function_exists('vmo_decrypt_credential') ? vmo_decrypt_credential((string) ($account['encrypted_password'] ?? '')) : '';
        if (!$email || !$mailbox_id || !$user_id || !$password) {
            return $this->record_sync_error($mailbox_id, $email, 'Missing mailbox credentials.');
        }

        if (function_exists('vmo_is_user_service_locked') && vmo_is_user_service_locked($user_id)) {
            return $this->record_sync_error($mailbox_id, $email, 'Mailbox sync is paused until billing is resolved.');
        }

        $host = sanitize_text_field((string) ($account['imap_host'] ?? ''));
        $port = intval($account['imap_port'] ?? 993) ?: 993;
        $mailboxes = array();
        if ($port === 993) {
            $mailboxes[] = sprintf('{%s:%d/imap/ssl}INBOX', $host, $port);
            $mailboxes[] = sprintf('{%s:%d/imap/ssl/novalidate-cert}INBOX', $host, $port);
        } else {
            $mailboxes[] = sprintf('{%s:%d/imap/tls}INBOX', $host, $port);
            $mailboxes[] = sprintf('{%s:%d/imap/notls}INBOX', $host, $port);
            $mailboxes[] = sprintf('{%s:%d/imap/novalidate-cert}INBOX', $host, $port);
        }

        $connection = false;
        $last_error = '';
        $open_flags = defined('OP_READONLY') ? OP_READONLY : 0;
        foreach ($mailboxes as $mailbox) {
            $connection = @imap_open($mailbox, $email, $password, $open_flags, 1);
            if ($connection) {
                break;
            }
            $last_error = function_exists('imap_last_error') ? (imap_last_error() ?: $last_error) : $last_error;
        }

        if (!$connection) {
            return $this->record_sync_error($mailbox_id, $email, $last_error ?: 'Could not connect to IMAP.');
        }

        $stored = 0;
        $last_sync_uid = intval($account['last_sync_uid'] ?? 0);
        $max_uid = $last_sync_uid;
        $search_flags = defined('SE_UID') ? SE_UID : 1;
        $criteria = $last_sync_uid > 0 ? ('UID ' . ($last_sync_uid + 1) . ':*') : 'ALL';
        $uids = @imap_search($connection, $criteria, $search_flags);
        if ($uids === false && $last_sync_uid > 0) {
            $uids = @imap_search($connection, 'ALL', $search_flags);
        }
        $uids = array_values(array_filter(array_map('intval', (array) $uids), function($uid) use ($last_sync_uid) {
            return $uid > $last_sync_uid;
        }));
        sort($uids, SORT_NUMERIC);
        $uids = array_slice($uids, -30);

        if (!class_exists('VMO_Email_Storage')) {
            $storage_file = dirname(__FILE__) . '/class-vmo-email-storage.php';
            if (file_exists($storage_file) && is_readable($storage_file)) {
                require_once $storage_file;
            }
        }
        $storage = class_exists('VMO_Email_Storage') ? new VMO_Email_Storage() : null;

        foreach ($uids as $uid) {
            $msg_no = intval(imap_msgno($connection, intval($uid)));
            if ($uid <= 0 || $msg_no <= 0) {
                continue;
            }
            $max_uid = max($max_uid, $uid);
            $external_id = 'imap:' . $mailbox_id . ':' . $uid;
            $emails_table = $wpdb->prefix . 'vmo_emails';
            $exists = $wpdb->get_var($wpdb->prepare("SELECT id FROM {$emails_table} WHERE external_id = %s LIMIT 1", $external_id));
            if ($exists) {
                continue;
            }

            $overview = imap_fetch_overview($connection, intval($msg_no), 0);
            $overview = is_array($overview) && isset($overview[0]) ? $overview[0] : null;
            $subject = $this->decode_header_text($overview->subject ?? '(No subject)');
            $from_email = $this->extract_first_email($overview->from ?? '');
            $date = !empty($overview->date) ? gmdate('Y-m-d H:i:s', strtotime((string) $overview->date)) : current_time('mysql');
            $body = $this->extract_message_body($connection, intval($msg_no));
            $is_read = !empty($overview->seen) ? 1 : 0;

            if ($storage) {
                $saved = $storage->save_email(array(
                    'user_id' => $user_id,
                    'mailbox_id' => $mailbox_id,
                    'folder' => 'inbox',
                    'type' => 'transactional',
                    'provider' => 'imap',
                    'external_id' => $external_id,
                    'from_email' => $from_email ?: 'unknown@example.com',
                    'to_email' => $email,
                    'subject' => sanitize_text_field($subject),
                    'message' => wp_kses_post($body),
                    'status' => 'received',
                    'is_read' => $is_read,
                    'created_at' => $date,
                    'updated_at' => current_time('mysql'),
                ));
                if ($saved) {
                    $email_id = intval($wpdb->insert_id);
                    $stored++;
                    if (function_exists('vmo_log_unified_action')) {
                        vmo_log_unified_action($user_id, 'email', array(
                            'title' => 'New email from ' . ($from_email ?: 'unknown sender'),
                            'description' => sanitize_text_field($subject),
                            'source_id' => $external_id,
                            'priority' => 'normal',
                            'category' => 'inbox',
                        ));
                    }
                    do_action('vmo_inbound_email_saved', $user_id, $mailbox_id, $email_id, $email, array(
                        'external_id' => $external_id,
                        'from_email' => $from_email ?: 'unknown@example.com',
                        'to_email' => $email,
                        'subject' => $subject,
                        'is_read' => $is_read,
                    ));
                }
            }
        }

        imap_close($connection);
        $wpdb->update($accounts_table, array(
            'last_sync_uid' => $max_uid,
            'last_synced_at' => current_time('mysql'),
            'last_sync_error' => '',
        ), array('id' => $mailbox_id));

        return array('email' => $email, 'stored' => $stored);
    }

    private function record_sync_error($mailbox_id, $email, $message) {
        global $wpdb;
        if ($mailbox_id > 0) {
            $wpdb->update($wpdb->prefix . 'vmo_email_accounts', array(
                'last_synced_at' => current_time('mysql'),
                'last_sync_error' => sanitize_text_field($message),
            ), array('id' => intval($mailbox_id)));
        }
        return array('email' => $email, 'stored' => 0, 'error' => $message);
    }

    private function decode_header_text($value) {
        $value = (string) $value;
        if (function_exists('imap_mime_header_decode')) {
            $parts = imap_mime_header_decode($value);
            if (is_array($parts)) {
                $out = '';
                foreach ($parts as $part) {
                    $text = (string) ($part->text ?? '');
                    $charset = strtoupper((string) ($part->charset ?? ''));
                    if ($charset && $charset !== 'DEFAULT' && function_exists('mb_convert_encoding')) {
                        $text = @mb_convert_encoding($text, 'UTF-8', $charset);
                    }
                    $out .= $text;
                }
                return $out ?: $value;
            }
        }
        return $value;
    }

    private function extract_first_email($value) {
        $value = (string) $value;
        if (function_exists('imap_rfc822_parse_adrlist')) {
            $addresses = @imap_rfc822_parse_adrlist($value, '');
            if (is_array($addresses) && !empty($addresses[0])) {
                $mailbox = $addresses[0]->mailbox ?? '';
                $host = $addresses[0]->host ?? '';
                $email = sanitize_email($mailbox && $host ? ($mailbox . '@' . $host) : '');
                if ($email) {
                    return $email;
                }
            }
        }
        if (preg_match('/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/i', $value, $m)) {
            return sanitize_email($m[0]);
        }
        return '';
    }

    private function extract_message_body($connection, $msg_no) {
        $structure = imap_fetchstructure($connection, $msg_no);
        if (!$structure) {
            return wp_strip_all_tags((string) imap_body($connection, $msg_no));
        }

        $html = $this->find_body_part($connection, $msg_no, $structure, '', 'HTML');
        if ($html !== '') {
            return $html;
        }
        $plain = $this->find_body_part($connection, $msg_no, $structure, '', 'PLAIN');
        if ($plain !== '') {
            return nl2br(esc_html($plain));
        }
        return nl2br(esc_html(wp_strip_all_tags((string) imap_body($connection, $msg_no))));
    }

    private function find_body_part($connection, $msg_no, $part, $part_no, $target_subtype) {
        $subtype = strtoupper((string) ($part->subtype ?? ''));
        if (empty($part->parts) && $subtype === $target_subtype) {
            $section = $part_no ?: '1';
            $body = imap_fetchbody($connection, $msg_no, $section);
            return $this->decode_part_body($body, intval($part->encoding ?? 0));
        }

        if (!empty($part->parts) && is_array($part->parts)) {
            foreach ($part->parts as $index => $child) {
                $child_no = $part_no === '' ? (string) ($index + 1) : ($part_no . '.' . ($index + 1));
                $body = $this->find_body_part($connection, $msg_no, $child, $child_no, $target_subtype);
                if ($body !== '') {
                    return $body;
                }
            }
        }

        return '';
    }

    private function decode_part_body($body, $encoding) {
        $body = (string) $body;
        if ($encoding === 3) {
            $body = base64_decode($body); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode -- email MIME decoding
        } elseif ($encoding === 4) {
            $body = quoted_printable_decode($body);
        }
        return trim((string) $body);
    }
}