<?php
/**
 * Fluent SMTP Importer.
 *
 * @since 1.0.0
 *
 * @package NimblyMail\SMTP
 */

namespace NimblyMail\SMTP\Import;

defined( 'ABSPATH' ) || exit;

/**
 * Class FluentSmtpImporter
 *
 * Imports email settings from the Fluent SMTP plugin into NimblyMail.
 * Reads from the `fluentmail-settings` option and maps the default
 * connection's provider_settings to the NimblyMail options format.
 *
 * @since 1.0.0
 * @since 1.0.4 Credentials that the source plugin stores scrambled are now
 *              unscrambled before being carried across, instead of being
 *              imported as unusable text, and Brevo credentials are now
 *              carried across too.
 * @since 1.0.6 Added Outlook / Microsoft 365 credential mapping, including
 *              its tenant ID.
 */
class FluentSmtpImporter extends AbstractImporter {

    /**
     * Get the importer slug identifier.
     *
     * @since 1.0.0
     *
     * @return string
     */
    public function get_slug(): string {
        return 'fluent-smtp';
    }

    /**
     * Get the human-readable name of the source plugin.
     *
     * @since 1.0.0
     *
     * @return string
     */
    public function get_name(): string {
        return __( 'Fluent SMTP', 'nimblymail-smtp' );
    }

    /**
     * Get a short description for the importer card UI.
     *
     * @since 1.0.0
     *
     * @return string
     */
    public function get_description(): string {
        return __( 'Import your email settings from Fluent SMTP.', 'nimblymail-smtp' );
    }

    /**
     * Get the wp_options key for the source plugin settings.
     *
     * @since 1.0.0
     *
     * @return string
     */
    public function get_option_key(): string {
        return 'fluentmail-settings';
    }

    /**
     * Get the plugin basename for active-plugin detection.
     *
     * @since 1.0.0
     *
     * @return string
     */
    public function get_plugin_basename(): string {
        return 'fluent-smtp/fluent-smtp.php';
    }

    /**
     * Extract the provider_settings array from the default connection.
     *
     * Uses `misc.default_connection` when it points to a valid connection key.
     * Falls back to the first connection in the array when the default is absent
     * or invalid.
     *
     * @since 1.0.0
     *
     * @param array $source_data The raw fluentmail-settings option value.
     *
     * @return array|null The provider_settings array, or null if none found.
     */
    private function get_default_connection( array $source_data ): ?array {
        $connections = $source_data['connections'] ?? array();

        if ( empty( $connections ) || ! is_array( $connections ) ) {
            return null;
        }

        $default_key = $source_data['misc']['default_connection'] ?? null;

        if ( $default_key && isset( $connections[ $default_key ]['provider_settings'] ) ) {
            return $connections[ $default_key ]['provider_settings'];
        }

        $first = reset( $connections );

        if ( isset( $first['provider_settings'] ) ) {
            return $first['provider_settings'];
        }

        return null;
    }

    /**
     * Decrypt a Fluent SMTP secret field, or determine it cannot be decrypted.
     *
     * Fluent SMTP encrypts one secret field per provider at rest by default
     * (top-level `use_encrypt`, defaulting to 'yes' via the
     * `fluentsmtp_use_encrypt` filter), unless the individual connection
     * opted out via `provider_settings.disable_encryption === 'yes'`. When a
     * field is encrypted, only Fluent SMTP's own `fluentMailEncryptDecrypt()`
     * function (defined by that plugin, not this one) can recover the
     * plaintext, so it is only usable while Fluent SMTP is active.
     *
     * @since 1.0.4
     *
     * @param array  $source_data       Full raw fluentmail-settings option array.
     * @param array  $provider_settings The current connection's provider_settings array.
     * @param string $raw_value         The raw field value as stored.
     *
     * @return string|null The plaintext value, or null if it is encrypted and cannot be decrypted.
     */
    private function decrypt_fluent_secret( array $source_data, array $provider_settings, string $raw_value ): ?string {
        $is_encrypted = ! empty( $source_data['use_encrypt'] )
            && 'yes' !== ( $provider_settings['disable_encryption'] ?? '' );

        if ( ! $is_encrypted ) {
            return $raw_value;
        }

        if ( ! function_exists( 'fluentMailEncryptDecrypt' ) ) {
            return null;
        }

        $decrypted = fluentMailEncryptDecrypt( $raw_value, 'd' );

        if ( ! is_string( $decrypted ) || '' === $decrypted ) {
            return null;
        }

        return $decrypted;
    }

    /**
     * Map Fluent SMTP source settings to the NimblyMail options format.
     *
     * Extracts the default connection's provider_settings and maps each
     * supported field. When `key_store === 'db'` (the default) credential
     * fields are read from the option payload; when `key_store ===
     * 'wp_config'` the value lives in a PHP constant on the source side and
     * cannot be read from the option payload — those fields are skipped with
     * a warning that names the corresponding NimblyMail constant the user
     * should define. Fluent SMTP additionally encrypts one secret field per
     * provider at rest by default (see decrypt_fluent_secret()); when it
     * cannot be decrypted the field is skipped with a warning rather than
     * importing ciphertext as if it were the real credential. Provider-
     * specific fields are only mapped when the provider is supported.
     * Also maps log retention from misc.log_saved_interval_days and alert
     * notification settings from the _fluent_smtp_notify_settings option.
     *
     * @since 1.0.0
     * @since 1.0.4 Corrected the claim that Fluent SMTP stores credentials
     *              unencrypted — it encrypts them by default. Each provider's
     *              secret is now decrypted before being carried across, and a
     *              secret that cannot be decrypted is skipped with a warning
     *              rather than imported as unusable scrambled text. Brevo
     *              credentials are now carried across, a mailer is only saved
     *              when its credentials actually came with it, and a source
     *              provider NimblyMail cannot handle now raises a warning
     *              instead of being passed over in silence.
     * @since 1.0.6 Outlook credentials, including its tenant ID, are now
     *              carried across.
     *
     * @param array $source_data Raw fluentmail-settings option array.
     *
     * @return array{settings: array, warnings: array, skipped: array}
     */
    protected function map_settings( array $source_data ): array {
        $settings = array();
        $warnings = array();
        $skipped  = array();

        $provider_settings = $this->get_default_connection( $source_data );

        if ( null === $provider_settings ) {
            return array(
                'settings' => $settings,
                'warnings' => $warnings,
                'skipped'  => $skipped,
            );
        }

        // --- mail group ---
        $mail = array();

        if ( isset( $provider_settings['sender_name'] ) ) {
            $mail['from_name'] = $provider_settings['sender_name'];
        }

        if ( isset( $provider_settings['sender_email'] ) ) {
            $mail['from_email'] = $provider_settings['sender_email'];
        }

        if ( isset( $provider_settings['force_from_name'] ) ) {
            $mail['from_name_force'] = $this->to_bool( $provider_settings['force_from_name'] );
        }

        if ( isset( $provider_settings['force_from_email'] ) ) {
            $mail['from_email_force'] = $this->to_bool( $provider_settings['force_from_email'] );
        }

        if ( isset( $provider_settings['return_path'] ) ) {
            $mail['return_path'] = $this->to_bool( $provider_settings['return_path'] );
        }

        $source_provider = $provider_settings['provider'] ?? null;
        $mailer_slug     = null;

        if ( $source_provider ) {
            $mailer_slug = $this->map_mailer_slug( $source_provider );

            if ( $mailer_slug ) {
                $mail['mailer'] = $mailer_slug;
            } else {
                $this->skip_unsupported_mailer( (string) $source_provider, $warnings, $skipped );
            }
        }

        if ( ! empty( $mail ) ) {
            $settings['mail'] = $mail;
        }

        // --- provider-specific groups ---
        $key_store = isset( $provider_settings['key_store'] ) ? (string) $provider_settings['key_store'] : 'db';
        $in_db     = ( 'db' === $key_store );

        if ( 'smtp' === $mailer_slug ) {
            $smtp = array();

            if ( isset( $provider_settings['host'] ) ) {
                $smtp['host'] = sanitize_text_field( $provider_settings['host'] );
            }

            if ( isset( $provider_settings['port'] ) ) {
                $smtp['port'] = (int) $provider_settings['port'];
            }

            if ( isset( $provider_settings['encryption'] ) ) {
                $smtp['encryption'] = sanitize_text_field( $provider_settings['encryption'] );
            }

            if ( isset( $provider_settings['auto_tls'] ) ) {
                $smtp['autotls'] = $this->to_bool( $provider_settings['auto_tls'] );
            }

            if ( isset( $provider_settings['auth'] ) ) {
                $smtp['auth'] = $this->to_bool( $provider_settings['auth'] );
            }

            if ( $in_db ) {
                if ( ! empty( $provider_settings['username'] ) ) {
                    $smtp['user'] = sanitize_text_field( $provider_settings['username'] );
                }

                if ( ! empty( $provider_settings['password'] ) ) {
                    $password = $this->decrypt_fluent_secret( $source_data, $provider_settings, (string) $provider_settings['password'] );

                    if ( null === $password ) {
                        $this->skip_encrypted( __( 'SMTP Password', 'nimblymail-smtp' ), 'smtp.pass', $warnings, $skipped );
                    } else {
                        $smtp['pass'] = $password;
                    }
                }
            } else {
                $this->skip_wpconfig( __( 'SMTP Username', 'nimblymail-smtp' ), 'smtp.user', 'NIMBLYMAIL_SMTP_USER', $warnings, $skipped );
                $this->skip_wpconfig( __( 'SMTP Password', 'nimblymail-smtp' ), 'smtp.pass', 'NIMBLYMAIL_SMTP_PASS', $warnings, $skipped );
            }

            if ( ! empty( $smtp ) ) {
                $settings['smtp'] = $smtp;
            }
        } elseif ( 'ses' === $mailer_slug ) {
            $ses = array();

            if ( isset( $provider_settings['region'] ) ) {
                $ses['region'] = sanitize_text_field( $provider_settings['region'] );
            }

            if ( $in_db ) {
                if ( ! empty( $provider_settings['access_key'] ) ) {
                    $ses['access_key_id'] = sanitize_text_field( $provider_settings['access_key'] );
                }

                if ( ! empty( $provider_settings['secret_key'] ) ) {
                    $secret_key = $this->decrypt_fluent_secret( $source_data, $provider_settings, (string) $provider_settings['secret_key'] );

                    if ( null === $secret_key ) {
                        $this->skip_encrypted( __( 'SES Secret Access Key', 'nimblymail-smtp' ), 'ses.secret_access_key', $warnings, $skipped );
                    } else {
                        $ses['secret_access_key'] = $secret_key;
                    }
                }
            } else {
                $this->skip_wpconfig( __( 'SES Access Key ID', 'nimblymail-smtp' ), 'ses.access_key_id', 'NIMBLYMAIL_SES_ACCESS_KEY_ID', $warnings, $skipped );
                $this->skip_wpconfig( __( 'SES Secret Access Key', 'nimblymail-smtp' ), 'ses.secret_access_key', 'NIMBLYMAIL_SES_SECRET_ACCESS_KEY', $warnings, $skipped );
            }

            if ( ! empty( $ses ) ) {
                $settings['ses'] = $ses;
            }
        } elseif ( 'gmail' === $mailer_slug ) {
            $gmail = array();

            if ( $in_db ) {
                if ( ! empty( $provider_settings['client_id'] ) ) {
                    $gmail['client_id'] = sanitize_text_field( $provider_settings['client_id'] );
                }

                if ( ! empty( $provider_settings['client_secret'] ) ) {
                    $client_secret = $this->decrypt_fluent_secret( $source_data, $provider_settings, (string) $provider_settings['client_secret'] );

                    if ( null === $client_secret ) {
                        $this->skip_encrypted( __( 'Gmail Client Secret', 'nimblymail-smtp' ), 'gmail.client_secret', $warnings, $skipped );
                    } else {
                        $gmail['client_secret'] = $client_secret;
                    }
                }
            } else {
                $this->skip_wpconfig( __( 'Gmail Client ID', 'nimblymail-smtp' ), 'gmail.client_id', 'NIMBLYMAIL_GMAIL_CLIENT_ID', $warnings, $skipped );
                $this->skip_wpconfig( __( 'Gmail Client Secret', 'nimblymail-smtp' ), 'gmail.client_secret', 'NIMBLYMAIL_GMAIL_CLIENT_SECRET', $warnings, $skipped );
            }

            // Tokens are not transferable across plugins regardless of key_store.
            $warnings[] = array(
                'field'   => 'gmail.oauth',
                'message' => __( 'Gmail OAuth access and refresh tokens cannot be transferred between plugins. Please reconnect your Google account inside NimblyMail settings after the import.', 'nimblymail-smtp' ),
            );

            if ( ! empty( $gmail ) ) {
                $settings['gmail'] = $gmail;
            }
        } elseif ( 'outlook' === $mailer_slug ) {
            $outlook = array();

            if ( $in_db ) {
                if ( ! empty( $provider_settings['client_id'] ) ) {
                    $outlook['client_id'] = sanitize_text_field( $provider_settings['client_id'] );
                }

                if ( ! empty( $provider_settings['client_secret'] ) ) {
                    $client_secret = $this->decrypt_fluent_secret( $source_data, $provider_settings, (string) $provider_settings['client_secret'] );

                    if ( null === $client_secret ) {
                        $this->skip_encrypted( __( 'Outlook Client Secret', 'nimblymail-smtp' ), 'outlook.client_secret', $warnings, $skipped );
                    } else {
                        $outlook['client_secret'] = $client_secret;
                    }
                }

                if ( ! empty( $provider_settings['tenant_id'] ) ) {
                    $outlook['tenant'] = sanitize_text_field( $provider_settings['tenant_id'] );
                }
            } else {
                $this->skip_wpconfig( __( 'Outlook Client ID', 'nimblymail-smtp' ), 'outlook.client_id', 'NIMBLYMAIL_OUTLOOK_CLIENT_ID', $warnings, $skipped );
                $this->skip_wpconfig( __( 'Outlook Client Secret', 'nimblymail-smtp' ), 'outlook.client_secret', 'NIMBLYMAIL_OUTLOOK_CLIENT_SECRET', $warnings, $skipped );
            }

            // Tokens are not transferable across plugins regardless of key_store.
            $warnings[] = array(
                'field'   => 'outlook.oauth',
                'message' => __( 'Outlook OAuth access and refresh tokens cannot be transferred between plugins. Please reconnect your Microsoft account inside NimblyMail settings after the import.', 'nimblymail-smtp' ),
            );

            if ( ! empty( $outlook ) ) {
                $settings['outlook'] = $outlook;
            }
        } elseif ( 'sendlayer' === $mailer_slug ) {
            if ( $in_db ) {
                if ( ! empty( $provider_settings['api_key'] ) ) {
                    $settings['sendlayer'] = array(
                        'api_key' => (string) $provider_settings['api_key'],
                    );
                }
            } else {
                $this->skip_wpconfig( __( 'SendLayer API Key', 'nimblymail-smtp' ), 'sendlayer.api_key', 'NIMBLYMAIL_SENDLAYER_API_KEY', $warnings, $skipped );
            }
        } elseif ( 'resend' === $mailer_slug ) {
            if ( $in_db ) {
                if ( ! empty( $provider_settings['api_key'] ) ) {
                    $settings['resend'] = array(
                        'api_key' => (string) $provider_settings['api_key'],
                    );
                }
            } else {
                $this->skip_wpconfig( __( 'Resend API Key', 'nimblymail-smtp' ), 'resend.api_key', 'NIMBLYMAIL_RESEND_API_KEY', $warnings, $skipped );
            }
        } elseif ( 'brevo' === $mailer_slug ) {
            if ( $in_db ) {
                if ( ! empty( $provider_settings['api_key'] ) ) {
                    $api_key = $this->decrypt_fluent_secret( $source_data, $provider_settings, (string) $provider_settings['api_key'] );

                    if ( null === $api_key ) {
                        $this->skip_encrypted( __( 'Brevo API Key', 'nimblymail-smtp' ), 'brevo.api_key', $warnings, $skipped );
                    } else {
                        $settings['brevo'] = array(
                            'api_key' => $api_key,
                        );
                    }
                }
            } else {
                $this->skip_wpconfig( __( 'Brevo API Key', 'nimblymail-smtp' ), 'brevo.api_key', 'NIMBLYMAIL_BREVO_API_KEY', $warnings, $skipped );
            }
        }

        // --- logs group ---
        $misc = $source_data['misc'] ?? array();
        $logs = array();

        if ( isset( $misc['log_emails'] ) ) {
            $logs['enabled'] = $this->to_bool( $misc['log_emails'] );
        }

        if ( ! empty( $logs ) ) {
            $settings['logs'] = $logs;
        }

        // --- rotation group ---
        if ( isset( $misc['log_saved_interval_days'] ) ) {
            $interval_days = (int) $misc['log_saved_interval_days'];

            if ( $interval_days > 0 ) {
                $settings['rotation'] = array(
                    'by_time'      => true,
                    'by_time_days' => $interval_days,
                );
            }
        }

        // --- alerts group ---
        $notify_settings = get_option( '_fluent_smtp_notify_settings', array() );

        if ( ! empty( $notify_settings ) && is_array( $notify_settings ) ) {
            $alerts = array();

            if ( isset( $notify_settings['enabled'] ) ) {
                $alerts['enabled'] = $this->to_bool( $notify_settings['enabled'] );
            }

            if ( isset( $notify_settings['notify_email'] ) ) {
                $email = $notify_settings['notify_email'];

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

                $alerts['email_to'] = $email;
            }

            if ( ! empty( $alerts ) ) {
                $settings['alerts'] = $alerts;
            }
        }

        $this->guard_mailer_has_credentials( $mailer_slug, $settings, $warnings, $skipped );

        return array(
            'settings' => $settings,
            'warnings' => $warnings,
            'skipped'  => $skipped,
        );
    }
}
