<?php
/**
 * Mail suppression list management.
 *
 * @package MailDoctor
 */

namespace MailDoctor;

use wpdb;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Handles bounce/complaint suppression records.
 */
class Suppression {


	/**
	 * Database table accessor.
	 *
	 * @global wpdb $wpdb
	 * @return string
	 */
	public static function table_name(): string {
		global $wpdb;

		return $wpdb->prefix . 'mail_doctor_suppression';
	}

	/**
	 * Create or update schema.
	 *
	 * @return void
	 */
	public static function install(): void {
		global $wpdb;

		$table   = self::table_name();
		$charset = $wpdb->get_charset_collate();

		$sql = "CREATE TABLE {$table} (
			email VARCHAR(191) NOT NULL,
			reason VARCHAR(64) NOT NULL,
			first_seen DATETIME NOT NULL,
			last_seen DATETIME NOT NULL,
			meta LONGTEXT NULL,
			PRIMARY KEY (email),
			KEY reason (reason),
			KEY last_seen (last_seen)
		) {$charset};";

		require_once ABSPATH . 'wp-admin/includes/upgrade.php';
		dbDelta( $sql );
	}

	/**
	 * Record a suppression for an email address.
	 *
	 * @param string               $email Email address.
	 * @param string               $reason Reason descriptor.
	 * @param array<string, mixed> $meta Additional metadata.
	 * @return void
	 */
	public static function record( string $email, string $reason, array $meta = array() ): void {
		global $wpdb;

		$email  = sanitize_email( $email );
		$reason = sanitize_key( $reason );

		if ( '' === $email || ! is_email( $email ) ) {
			return;
		}

		if ( '' === $reason ) {
			$reason = 'unknown';
		}

		$now = gmdate( 'Y-m-d H:i:s' );

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$wpdb->replace(
			self::table_name(),
			array(
				'email'      => $email,
				'reason'     => $reason,
				'first_seen' => $now,
				'last_seen'  => $now,
				'meta'       => empty( $meta ) ? null : wp_json_encode( $meta ),
			),
			array(
				'%s',
				'%s',
				'%s',
				'%s',
				'%s',
			)
		);
	}

	/**
	 * Update metadata for existing entry.
	 *
	 * @param string               $email Email address.
	 * @param array<string, mixed> $meta  Additional metadata merged into existing payload.
	 * @return void
	 */
	public static function merge_meta( string $email, array $meta ): void {
		global $wpdb;

		$email = sanitize_email( $email );

		if ( '' === $email || ! is_email( $email ) ) {
			return;
		}

		$table = self::table_name();

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$row = $wpdb->get_row(
			$wpdb->prepare(
				// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared
				"SELECT meta FROM {$table} WHERE email = %s",
				$email
			),
			ARRAY_A
		);

		$current = array();

		if ( $row && ! empty( $row['meta'] ) ) {
			$decoded = json_decode( (string) $row['meta'], true );
			if ( is_array( $decoded ) ) {
				$current = $decoded;
			}
		}

		$merged = array_merge( $current, $meta );

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$wpdb->update(
			self::table_name(),
			array(
				'meta'      => wp_json_encode( $merged ),
				'last_seen' => gmdate( 'Y-m-d H:i:s' ),
			),
			array( 'email' => $email ),
			array( '%s', '%s' ),
			array( '%s' )
		);
	}

	/**
	 * Remove email from suppression list.
	 *
	 * @param string $email Email address.
	 * @return void
	 */
	public static function clear( string $email ): void {
		global $wpdb;

		$email = sanitize_email( $email );

		if ( '' === $email || ! is_email( $email ) ) {
			return;
		}

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$wpdb->delete(
			self::table_name(),
			array( 'email' => $email ),
			array( '%s' )
		);
	}

	/**
	 * Determine if email is suppressed.
	 *
	 * @param string $email Email address.
	 * @return bool
	 */
	public static function is_suppressed( string $email ): bool {
		global $wpdb;

		$email = sanitize_email( $email );

		if ( '' === $email || ! is_email( $email ) ) {
			return false;
		}

		$table = self::table_name();

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$count = $wpdb->get_var(
			$wpdb->prepare(
				// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared
				"SELECT COUNT(*) FROM {$table} WHERE email = %s",
				$email
			)
		);

		return (int) $count > 0;
	}

	/**
	 * Filter list of recipients returning suppressed entries.
	 *
	 * @param array<int|string> $emails Recipient list (string or array).
	 * @return array<string, string> Map of email => reason.
	 */
	public static function filter_suppressed( array $emails ): array {
		global $wpdb;

		if ( empty( $emails ) ) {
			return array();
		}

		$emails = array_unique(
			array_filter(
				array_map( 'sanitize_email', $emails ),
				static function ( $email ) {
					return '' !== $email && is_email( $email );
				}
			)
		);

		if ( empty( $emails ) ) {
			return array();
		}

		$placeholders = implode( ',', array_fill( 0, count( $emails ), '%s' ) );
		$table        = self::table_name();

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$rows = $wpdb->get_results(
			$wpdb->prepare(
				// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
				"SELECT email, reason FROM {$table} WHERE email IN ({$placeholders})",
				$emails
			),
			ARRAY_A
		);

		if ( empty( $rows ) ) {
			return array();
		}

		$result = array();

		foreach ( $rows as $row ) {
			if ( empty( $row['email'] ) ) {
				continue;
			}

			$result[ $row['email'] ] = $row['reason'] ?? 'suppressed';
		}

		return $result;
	}

	/**
	 * Return suppression entry.
	 *
	 * @param string $email Email address.
	 * @return array<string, mixed>|null
	 */
	public static function get( string $email ): ?array {
		global $wpdb;

		$email = sanitize_email( $email );

		if ( '' === $email || ! is_email( $email ) ) {
			return null;
		}

		$table = self::table_name();

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$row = $wpdb->get_row(
			$wpdb->prepare(
				// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared
				"SELECT * FROM {$table} WHERE email = %s",
				$email
			),
			ARRAY_A
		);

		if ( ! $row ) {
			return null;
		}

		$meta = array();
		if ( ! empty( $row['meta'] ) ) {
			$decoded = json_decode( (string) $row['meta'], true );
			if ( is_array( $decoded ) ) {
				$meta = $decoded;
			}
		}

		return array(
			'email'      => $row['email'],
			'reason'     => $row['reason'],
			'first_seen' => $row['first_seen'],
			'last_seen'  => $row['last_seen'],
			'meta'       => $meta,
		);
	}
}
