<?php
/**
 * Abstract Mailer base class.
 *
 * @package NimblyMail\SMTP
 */

namespace NimblyMail\SMTP\Mailers;

use NimblyMail\SMTP\Options;
use PHPMailer\PHPMailer\PHPMailer;

defined( 'ABSPATH' ) || exit;

/**
 * Class AbstractMailer
 *
 * Base class for all mail transports.
 *
 * @since 1.0.0
 */
abstract class AbstractMailer {

    /**
     * Get the mailer slug/ID.
     *
     * @return string
     */
    abstract public function get_slug(): string;

    /**
     * Get the mailer readable title.
     *
     * @return string
     */
    abstract public function get_title(): string;

    /**
     * Get the mailer description.
     *
     * @return string
     */
    abstract public function get_description(): string;

    /**
     * Get the settings schema for this mailer as a section-shaped array.
     *
     * Returns a single section descriptor consumed by SettingsProvider to
     * build the Connection tab dynamically. The section shape is:
     *
     *   array(
     *     'id'          => '<slug>_settings',
     *     'title'       => __( '...', 'nimblymail-smtp' ),
     *     'description' => __( '...', 'nimblymail-smtp' ),   // optional
     *     'fields'      => array( /* field defs with group key set * / ),
     *   )
     *
     * Mailers with no configurable settings (e.g. DefaultMailer) MUST return
     * an empty array so the SettingsProvider loop can skip them cleanly.
     *
     * @since 1.0.0 Standardized to section-shaped return for SettingsProvider loop.
     *
     * @return array Section descriptor array, or empty array if no settings.
     */
    abstract public function get_settings_fields(): array;

    /**
     * Configure the PHPMailer instance for this transport.
     *
     * @since 1.0.0
     *
     * @param PHPMailer $phpmailer The PHPMailer instance.
     *
     * @return void
     */
    abstract public function configure( PHPMailer $phpmailer ): void;

    /**
     * Check if the mailer has all required settings configured.
     *
     * Reads required fields from the section-shaped return of get_settings_fields().
     *
     * @since 1.0.0
     *
     * @return bool True if all required fields have values.
     */
    public function is_ready(): bool {
        $section = $this->get_settings_fields();
        $fields  = $section['fields'] ?? array();

        foreach ( $fields as $field ) {
            if ( ! empty( $field['required'] ) ) {
                $value = $this->get_option( $field['id'] );
                if ( empty( $value ) ) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * Get a setting value for this mailer.
     *
     * @since 1.0.0
     *
     * @param string $key The setting key.
     *
     * @return mixed
     */
    public function get_option( string $key ): mixed {
        return Options::init()->get( $this->get_slug(), $key );
    }

    /**
     * Get this mailer's deliverability audit requirements.
     *
     * Each built-in mailer overrides this with the SPF include token(s) and
     * DKIM selector(s) the audit should expect to find published on the
     * sending domain. The HealthAuditor uses these to score the SPF and DKIM
     * pillars — an empty array on either key turns that pillar into an
     * informational/Skipped check (selector unknown, or include unconstrained).
     *
     * Shape:
     *
     *   array(
     *     'spf_includes'   => array<string>, // e.g. [ 'spf.brevo.com' ]
     *     'dkim_selectors' => array<string>, // e.g. [ 'mail' ]
     *     'docs_url'       => string,        // provider DNS-setup docs URL
     *   )
     *
     * @since 1.0.0
     *
     * @return array{spf_includes: string[], dkim_selectors: string[], docs_url: string}
     */
    public function get_audit_requirements(): array {
        return array(
            'spf_includes'   => array(),
            'dkim_selectors' => array(),
            'docs_url'       => '',
        );
    }

    /**
     * Get the external endpoints / ports this mailer connects to.
     *
     * The HealthAuditor uses these to verify that the server's firewall or
     * proxy allows outbound traffic to the provider.
     *
     * Shape:
     *
     *   array(
     *     array(
     *       'type' => 'smtp',
     *       'host' => 'smtp.gmail.com',
     *       'port' => 587,
     *     ),
     *     array(
     *       'type' => 'api',
     *       'url'  => 'https://api.brevo.com/v3',
     *     ),
     *   )
     *
     * @since 1.0.0
     *
     * @return array<array<string, mixed>>
     */
    public function get_connectivity_endpoints(): array {
        return array();
    }

    /**
     * Return validation errors if not ready.
     *
     * Reads required fields from the section-shaped return of get_settings_fields().
     *
     * @since 1.0.0
     *
     * @return array Array of error messages.
     */
    public function validate_settings(): array {
        $errors  = array();
        $section = $this->get_settings_fields();
        $fields  = $section['fields'] ?? array();

        foreach ( $fields as $field ) {
            if ( ! empty( $field['required'] ) && empty( $this->get_option( $field['id'] ) ) ) {
                /* translators: %s: Field title. */
                $errors[] = sprintf( __( '%s is required.', 'nimblymail-smtp' ), $field['title'] );
            }
        }
        return $errors;
    }
}
