<?php
/**
 * Per-pillar audit result data object.
 *
 * @package NimblyMail\SMTP
 */

namespace NimblyMail\SMTP\Audit;

defined( 'ABSPATH' ) || exit;

/**
 * Class PillarResult
 *
 * Immutable value object representing the result of a single deliverability
 * pillar (SPF, DKIM, DMARC, PTR). Carries the status, a human-readable
 * message, the raw records observed, and an optional "expected" record value
 * the user can publish to fix a Fail/Warn state.
 *
 * @since 1.0.0
 */
final class PillarResult {

    /**
     * Pillar passed all checks.
     *
     * @since 1.0.0
     *
     * @var string
     */
    public const STATUS_PASS = 'pass';

    /**
     * Pillar passed with caveats (e.g. DMARC p=none).
     *
     * @since 1.0.0
     *
     * @var string
     */
    public const STATUS_WARN = 'warn';

    /**
     * Pillar failed and needs user action.
     *
     * @since 1.0.0
     *
     * @var string
     */
    public const STATUS_FAIL = 'fail';

    /**
     * Pillar was not evaluated (not applicable to active mailer).
     *
     * @since 1.0.0
     *
     * @var string
     */
    public const STATUS_SKIPPED = 'skipped';

    /**
     * Pillar status. One of pass|warn|fail|skipped.
     *
     * @since 1.0.0
     *
     * @var string
     */
    private string $status;

    /**
     * Human-readable summary message.
     *
     * @since 1.0.0
     *
     * @var string
     */
    private string $message;

    /**
     * Raw DNS records observed during the check (numerical array of strings).
     *
     * @since 1.0.0
     *
     * @var string[]
     */
    private array $records;

    /**
     * Suggested record value the user should publish to remediate.
     *
     * Null when no specific suggestion is available (e.g. Pass state).
     *
     * @since 1.0.0
     *
     * @var string|null
     */
    private ?string $fix;

    /**
     * Constructor.
     *
     * @since 1.0.0
     *
     * @param string      $status  Pillar status — one of the STATUS_* constants.
     * @param string      $message Human-readable summary.
     * @param string[]    $records Raw DNS records observed.
     * @param string|null $fix     Optional suggested record value.
     */
    public function __construct( string $status, string $message, array $records = array(), ?string $fix = null ) {
        $this->status  = $status;
        $this->message = $message;
        $this->records = array_values( array_map( 'strval', $records ) );
        $this->fix     = $fix;
    }

    /**
     * Get the pillar status.
     *
     * @since 1.0.0
     *
     * @return string
     */
    public function get_status(): string {
        return $this->status;
    }

    /**
     * Get the human-readable message.
     *
     * @since 1.0.0
     *
     * @return string
     */
    public function get_message(): string {
        return $this->message;
    }

    /**
     * Get the raw DNS records observed.
     *
     * @since 1.0.0
     *
     * @return string[]
     */
    public function get_records(): array {
        return $this->records;
    }

    /**
     * Get the suggested fix value.
     *
     * @since 1.0.0
     *
     * @return string|null
     */
    public function get_fix(): ?string {
        return $this->fix;
    }

    /**
     * Serialize to an array suitable for option storage and REST responses.
     *
     * @since 1.0.0
     *
     * @return array{status: string, message: string, records: string[], fix: ?string}
     */
    public function to_array(): array {
        return array(
            'status'  => $this->status,
            'message' => $this->message,
            'records' => $this->records,
            'fix'     => $this->fix,
        );
    }

    /**
     * Hydrate a PillarResult from an array (inverse of to_array).
     *
     * Falls back to a Skipped result when the input is malformed so the
     * caller never has to defend against partial snapshots.
     *
     * @since 1.0.0
     *
     * @param array $data Serialized pillar.
     *
     * @return self
     */
    public static function from_array( array $data ): self {
        $status  = isset( $data['status'] ) && is_string( $data['status'] ) ? $data['status'] : self::STATUS_SKIPPED;
        $message = isset( $data['message'] ) && is_string( $data['message'] ) ? $data['message'] : '';
        $records = isset( $data['records'] ) && is_array( $data['records'] ) ? $data['records'] : array();
        $fix     = isset( $data['fix'] ) && is_string( $data['fix'] ) ? $data['fix'] : null;

        return new self( $status, $message, $records, $fix );
    }
}
