<?php
/**
 * Newsletter campaign CRUD + queue setup.
 *
 * @package SuiteMkt3k
 */

namespace SuiteMkt3k\Modules\Newsletter;

use SuiteMkt3k\Email\Email_Renderer;
use SuiteMkt3k\Email\Mailer;
use SuiteMkt3k\Installer;
use SuiteMkt3k\Modules\Subscribers\Subscriber_Manager;
use SuiteMkt3k\Security\Capabilities;
use SuiteMkt3k\Security\Nonces;
use SuiteMkt3k\Utils\Logger;
use SuiteMkt3k\Utils\Sanitizer;
use SuiteMkt3k\Utils\Validator;

defined( 'ABSPATH' ) || exit;
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching

final class Newsletter_Manager {

	public const STATUS_DRAFT     = 'draft';
	public const STATUS_SCHEDULED = 'scheduled';
	public const STATUS_SENDING   = 'sending';
	public const STATUS_SENT      = 'sent';

	public static function statuses(): array {
		return array(
			self::STATUS_DRAFT     => __( 'Draft', 'suitemkt3k' ),
			self::STATUS_SCHEDULED => __( 'Scheduled', 'suitemkt3k' ),
			self::STATUS_SENDING   => __( 'Sending', 'suitemkt3k' ),
			self::STATUS_SENT      => __( 'Sent', 'suitemkt3k' ),
		);
	}

	public static function status_label( string $status ): string {
		$map = self::statuses();
		return $map[ $status ] ?? $status;
	}

	public static function register(): void {
		add_action( 'admin_post_suitemkt3k_newsletter_save', array( __CLASS__, 'handle_save' ) );
		add_action( 'admin_post_suitemkt3k_newsletter_delete', array( __CLASS__, 'handle_delete' ) );
		add_action( 'admin_post_suitemkt3k_newsletter_duplicate', array( __CLASS__, 'handle_duplicate' ) );
		add_action( 'admin_post_suitemkt3k_newsletter_test', array( __CLASS__, 'handle_test' ) );
		add_action( 'admin_post_suitemkt3k_newsletter_send', array( __CLASS__, 'handle_send' ) );
	}

	public static function table(): string {
		return Installer::table( 'newsletters' );
	}

	public static function recipients_table(): string {
		return Installer::table( 'newsletter_recipients' );
	}

	public static function default_content(): array {
		$preset = \SuiteMkt3k\Email\Newsletter_Template_Presets::get( 'classic' );
		return array(
			'heading'         => $preset['heading'],
			'preheader'       => $preset['preheader'],
			'body'            => $preset['body'],
			'cta_label'       => $preset['cta_label'],
			'cta_url'         => $preset['cta_url'],
			'image_id'        => 0,
			'image_url'       => '',
			'image_alt'       => '',
			'accent'           => $preset['accent'],
			'header_color'     => $preset['header_color'] ?? $preset['accent'],
			'secondary_accent' => $preset['secondary_accent'],
			'badge_label'      => $preset['badge_label'],
			'cta_radius'       => $preset['cta_radius'],
			'footer_text'      => '',
			'unsubscribe_text' => $preset['unsubscribe_text'],
			'unsubscribe_cta'  => $preset['unsubscribe_cta'],
			'footer_color'     => $preset['footer_color'] ?? '#94A3B8',
		);
	}

	public static function list( int $limit = 100 ): array {
		global $wpdb;
		$limit = max( 1, min( 500, $limit ) );
		$rows = $wpdb->get_results(
			$wpdb->prepare( 'SELECT * FROM %i ORDER BY created_at DESC LIMIT %d', self::table(), $limit ),
			ARRAY_A
		);
		return is_array( $rows ) ? $rows : array();
	}

	public static function get( int $id ): ?array {
		global $wpdb;
		$row = $wpdb->get_row(
			$wpdb->prepare( 'SELECT * FROM %i WHERE id = %d', self::table(), $id ),
			ARRAY_A
		);
		if ( ! $row ) {
			return null;
		}
		$row['content'] = self::merge_content( array() );
		if ( ! empty( $row['content_json'] ) ) {
			$decoded = json_decode( (string) $row['content_json'], true );
			if ( is_array( $decoded ) ) {
				$row['content'] = self::merge_content( $decoded );
			}
		}
		unset( $row['content_json'] );
		return $row;
	}

	/**
	 * Merge saved content with defaults without empty strings wiping stored values.
	 *
	 * @param array<string, mixed> $saved
	 * @return array<string, mixed>
	 */
	public static function merge_content( array $saved ): array {
		$content = self::default_content();
		foreach ( $saved as $key => $value ) {
			if ( is_string( $value ) && '' === trim( $value ) ) {
				continue;
			}
			$content[ $key ] = $value;
		}
		return $content;
	}

	/**
	 * Build save payload from admin form POST (or legacy JSON payload field).
	 *
	 * @return array<string, mixed>
	 */
	private static function payload_from_post(): array {
		$decoded = Sanitizer::json_post( 'payload' );
		if ( ! empty( $decoded ) ) {
			return $decoded;
		}

		// Nested arrays are not available via filter_input; sanitize manually.
		$content = array();
		if ( isset( $_POST['content'] ) && is_array( $_POST['content'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce validated in handle_save() before calling this method
			$content = Sanitizer::recursive_text( wp_unslash( $_POST['content'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- nested builder array sanitized recursively
		}
		$name_raw      = Sanitizer::post( 'name', 'text' );
		$subject_raw   = Sanitizer::post( 'subject', 'text' );
		$template_raw  = Sanitizer::post( 'template_key', 'key' );
		$status_raw    = Sanitizer::post( 'status', 'key' );
		$scheduled_raw = Sanitizer::post( 'scheduled_at', 'text' );

		$image_id = isset( $content['image_id'] ) ? (int) $content['image_id'] : 0;
		if ( $image_id > 0 && empty( $content['image_url'] ) ) {
			$content['image_url'] = (string) ( wp_get_attachment_image_url( $image_id, 'large' ) ?: '' );
		}

		return array(
			'name'         => sanitize_text_field( $name_raw ),
			'subject'      => sanitize_text_field( $subject_raw ),
			'template_key' => sanitize_key( '' !== $template_raw ? $template_raw : 'classic' ),
			'status'       => sanitize_key( '' !== $status_raw ? $status_raw : self::STATUS_DRAFT ),
			'scheduled_at' => sanitize_text_field( $scheduled_raw ),
			'content'      => $content,
		);
	}

	public static function save( array $data, int $id = 0 ): array {
		global $wpdb;
		$template = Sanitizer::slug( $data['template_key'] ?? 'classic' );
		if ( ! Validator::is_in( $template, array_keys( Email_Renderer::templates() ) ) ) {
			$template = 'classic';
		}
		$status = Sanitizer::slug( $data['status'] ?? self::STATUS_DRAFT );
		if ( ! Validator::is_in( $status, array( self::STATUS_DRAFT, self::STATUS_SCHEDULED ) ) ) {
			$status = self::STATUS_DRAFT;
		}
		$content = isset( $data['content'] ) && is_array( $data['content'] ) ? Sanitizer::recursive_text( $data['content'] ) : array();
		$content = self::merge_content( $content );

		$row = array(
			'name'         => Sanitizer::text( $data['name'] ?? '', 190 ) ?: __( 'Untitled newsletter', 'suitemkt3k' ),
			'subject'      => Sanitizer::text( $data['subject'] ?? '', 255 ),
			'preheader'    => Sanitizer::text( $content['preheader'] ?? '', 255 ),
			'template_key' => $template,
			'content_json' => wp_json_encode( $content ),
			'status'       => $status,
			'scheduled_at' => Sanitizer::datetime( $data['scheduled_at'] ?? '' ),
		);

		if ( $id > 0 ) {
			$wpdb->update( self::table(), $row, array( 'id' => $id ) );
		} else {
			$wpdb->insert( self::table(), $row );
			$id = (int) $wpdb->insert_id;
		}
		return (array) self::get( $id );
	}

	public static function delete( int $id ): bool {
		global $wpdb;
		$wpdb->delete( self::recipients_table(), array( 'newsletter_id' => $id ), array( '%d' ) );
		return false !== $wpdb->delete( self::table(), array( 'id' => $id ), array( '%d' ) );
	}

	public static function duplicate( int $id ): ?array {
		$src = self::get( $id );
		if ( ! $src ) {
			return null;
		}
		return self::save(
			array(
				'name'         => $src['name'] . ' ' . __( '(copy)', 'suitemkt3k' ),
				'subject'      => $src['subject'],
				'template_key' => $src['template_key'],
				'status'       => self::STATUS_DRAFT,
				'scheduled_at' => '',
				'content'      => $src['content'] ?? array(),
			),
			0
		);
	}

	public static function render_for_subscriber( array $campaign, array $subscriber, string $tracking_hash ): string {
		$content = $campaign['content'] ?? self::default_content();
		$replacements = array(
			'{{name}}'    => $subscriber['name'] ?: '',
			'{{email}}'   => $subscriber['email'] ?? '',
			'{{first_name}}' => self::first_name( $subscriber['name'] ?? '' ),
		);
		$body = strtr( (string) $content['body'], $replacements );
		$cta_url = (string) $content['cta_url'];
		return Email_Renderer::render(
			$campaign['template_key'] ?? 'classic',
			array(
				'heading'          => $content['heading'] ?? '',
				'preheader'        => $content['preheader'] ?? '',
				'body'             => $body,
				'cta_label'        => $content['cta_label'] ?? '',
				'cta_url'          => $cta_url,
				'image_url'        => $content['image_url'] ?? '',
				'accent'           => $content['accent'] ?? '#2AB2E5',
				'header_color'     => $content['header_color'] ?? ( $content['accent'] ?? '#2AB2E5' ),
				'secondary_accent' => $content['secondary_accent'] ?? ( $content['accent'] ?? '#2AB2E5' ),
				'badge_label'      => $content['badge_label'] ?? '',
				'cta_radius'       => (int) ( $content['cta_radius'] ?? 10 ),
				'footer_color'     => $content['footer_color'] ?? '#94A3B8',
				'footer_text'      => $content['footer_text'] ?? '',
				'unsubscribe_text' => $content['unsubscribe_text'] ?? '',
				'unsubscribe_cta'  => $content['unsubscribe_cta'] ?? __( 'Unsubscribe', 'suitemkt3k' ),
				'unsubscribe_url'  => Subscriber_Manager::unsubscribe_url( $subscriber['unsubscribe_token'] ?? '' ),
			)
		);
	}

	private static function first_name( string $name ): string {
		$parts = preg_split( '/\s+/', trim( $name ) );
		return $parts ? (string) $parts[0] : '';
	}

	public static function handle_save(): void {
		Capabilities::ensure( Capabilities::EDIT_CAMPAIGN );
		check_admin_referer( Nonces::ADMIN_ACTION, '_wpnonce' );
		$id_raw  = filter_input( INPUT_POST, 'id', FILTER_SANITIZE_NUMBER_INT );
		$id      = max( 0, (int) $id_raw );
		$payload = self::payload_from_post();
		$row = self::save( $payload, $id );
		wp_safe_redirect( add_query_arg( array( 'view' => 'edit', 'id' => (int) $row['id'], 'updated' => '1' ), admin_url( 'admin.php?page=suitemkt3k-newsletter' ) ) );
		exit;
	}

	public static function handle_delete(): void {
		Capabilities::ensure( Capabilities::EDIT_CAMPAIGN );
		check_admin_referer( Nonces::ADMIN_ACTION, '_wpnonce' );
		$id_raw = filter_input( INPUT_POST, 'id', FILTER_SANITIZE_NUMBER_INT );
		$id     = max( 0, (int) $id_raw );
		if ( $id ) {
			self::delete( $id );
		}
		wp_safe_redirect( admin_url( 'admin.php?page=suitemkt3k-newsletter&deleted=1' ) );
		exit;
	}

	public static function handle_duplicate(): void {
		Capabilities::ensure( Capabilities::EDIT_CAMPAIGN );
		check_admin_referer( Nonces::ADMIN_ACTION, '_wpnonce' );
		$id_raw = filter_input( INPUT_POST, 'id', FILTER_SANITIZE_NUMBER_INT );
		$id     = max( 0, (int) $id_raw );
		$row = $id ? self::duplicate( $id ) : null;
		$target = $row
			? add_query_arg( array( 'view' => 'edit', 'id' => (int) $row['id'] ), admin_url( 'admin.php?page=suitemkt3k-newsletter' ) )
			: admin_url( 'admin.php?page=suitemkt3k-newsletter' );
		wp_safe_redirect( $target );
		exit;
	}

	public static function handle_test(): void {
		Capabilities::ensure( Capabilities::EDIT_CAMPAIGN );
		check_admin_referer( Nonces::ADMIN_ACTION, '_wpnonce' );

		$id = max( 0, (int) Sanitizer::post( 'id', 'int' ) );
		$to = Sanitizer::email( Sanitizer::post( 'test_email', 'email' ) );
		$row   = $id ? self::get( $id ) : null;
		if ( ! $row || '' === $to ) {
			wp_safe_redirect( add_query_arg( array( 'view' => 'edit', 'id' => $id, 'test' => 'failed' ), admin_url( 'admin.php?page=suitemkt3k-newsletter' ) ) );
			exit;
		}
		$hash       = bin2hex( random_bytes( 32 ) );
		$subscriber = array(
			'name'              => __( 'Test recipient', 'suitemkt3k' ),
			'email'             => $to,
			'unsubscribe_token' => str_repeat( '0', 64 ),
		);
		$html = self::render_for_subscriber( $row, $subscriber, $hash );
		$mailer = new Mailer();
		$ok = $mailer->send(
			array(
				'to'               => $to,
				'subject'          => '[TEST] ' . $row['subject'],
				'html'             => $html,
				'context'          => 'newsletter_test',
				'entity_id'        => $id,
				'tracking_hash'    => $hash,
				'unsubscribe_url'  => home_url( '/' ),
			)
		);
		wp_safe_redirect( add_query_arg( array( 'view' => 'edit', 'id' => $id, 'test' => $ok ? 'sent' : 'failed' ), admin_url( 'admin.php?page=suitemkt3k-newsletter' ) ) );
		exit;
	}

	public static function handle_send(): void {
		Capabilities::ensure( Capabilities::EDIT_CAMPAIGN );
		check_admin_referer( Nonces::ADMIN_ACTION, '_wpnonce' );
		$id_raw = filter_input( INPUT_POST, 'id', FILTER_SANITIZE_NUMBER_INT );
		$id     = max( 0, (int) $id_raw );
		if ( $id ) {
			$queued = Newsletter_Sender::queue_campaign( $id );
			if ( $queued > 0 ) {
				Newsletter_Sender::process_batch( $id );
				if ( ! wp_next_scheduled( 'suitemkt3k_send_newsletter_batch', array( $id ) ) ) {
					spawn_cron();
				}
			}
		}
		wp_safe_redirect( add_query_arg( array( 'view' => 'edit', 'id' => $id, 'queued' => '1' ), admin_url( 'admin.php?page=suitemkt3k-newsletter' ) ) );
		exit;
	}
}
