<?php

namespace WPVOID\StockBeacon\Notifications;

use WPVOID\StockBeacon\Core\WaitlistManager;
use WPVOID\StockBeacon\Settings;
use DateTimeImmutable;
if ( !defined( 'ABSPATH' ) ) {
    exit;
}
class EmailManager {
    private WaitlistManager $manager;

    /** @var bool Avoid duplicate cron / mail hooks when a second instance is constructed (e.g. REST double opt-in). */
    private static bool $hooks_registered = false;

    private ?\WP_Error $last_mail_error = null;

    public function __construct( WaitlistManager $manager ) {
        $this->manager = $manager;
        if ( self::$hooks_registered ) {
            return;
        }
        self::$hooks_registered = true;
        add_action(
            'stockbeacon_send_restock_batch',
            array($this, 'send_batch'),
            10,
            3
        );
        add_filter( 'wp_mail_from_name', array($this, 'filter_wp_mail_from_name') );
    }

    /**
     * @param string|mixed $from Default from name.
     * @return string|mixed
     */
    public function filter_wp_mail_from_name( $from ) {
        $custom = trim( (string) (Settings::get()['email_from_name'] ?? '') );
        return ( '' !== $custom ? $custom : $from );
    }

    /**
     * @param array<int, string> $headers
     */
    private function send_mail(
        string $to,
        string $subject,
        string $body,
        array $headers
    ) : bool {
        $this->last_mail_error = null;
        $on_failed = function ( $error ) : void {
            if ( $error instanceof \WP_Error ) {
                $this->last_mail_error = $error;
            }
        };
        add_action( 'wp_mail_failed', $on_failed );
        try {
            return (bool) wp_mail(
                $to,
                $subject,
                $body,
                $headers
            );
        } finally {
            remove_action( 'wp_mail_failed', $on_failed );
        }
    }

    private function get_last_mail_error_message() : string {
        if ( $this->last_mail_error instanceof \WP_Error ) {
            $message = trim( (string) $this->last_mail_error->get_error_message() );
            if ( '' !== $message ) {
                return $message;
            }
        }
        return __( 'WordPress mailer returned false.', 'stockbeacon-product-waitlists-for-woocommerce' );
    }

    /**
     * @param array<string, mixed> $meta
     */
    private function log_email_activity(
        int $subscription_id,
        string $event,
        string $email,
        string $email_type,
        string $subject,
        bool $sent,
        array $meta = array()
    ) : void {
        $meta = array_merge( $meta, array(
            'email'           => sanitize_email( $email ),
            'email_type'      => sanitize_key( $email_type ),
            'subject'         => sanitize_text_field( $subject ),
            'delivery_status' => ( $sent ? 'delivered' : 'failed' ),
        ) );
        if ( !$sent && empty( $meta['delivery_error'] ) ) {
            $meta['delivery_error'] = $this->get_last_mail_error_message();
        }
        $this->manager->log_event( $subscription_id, $event, $meta );
    }

    public function schedule_restock_batch( int $product_id, int $variation_id = 0 ) : void {
        $when = time();
        wp_schedule_single_event( $when, 'stockbeacon_send_restock_batch', array($product_id, $variation_id, 0) );
    }

    /**
     * Whether the subscription’s line item is still in stock (avoids mailing after inventory changed during the send delay).
     */
    private function subscription_line_is_in_stock( object $subscription ) : bool {
        $pid = (int) $subscription->product_id;
        $vid = (int) ($subscription->variation_id ?? 0);
        if ( $vid > 0 ) {
            $v = wc_get_product( $vid );
            return $v instanceof \WC_Product && $v->is_in_stock();
        }
        $p = wc_get_product( $pid );
        return $p instanceof \WC_Product && $p->is_in_stock();
    }

    public function send_batch( int $product_id, int $variation_id, int $after_id ) : void {
        $lock_key = 'stockbeacon_restock_lock_' . $product_id . '_' . $variation_id;
        if ( 0 === $after_id ) {
            if ( get_transient( $lock_key ) ) {
                return;
            }
            set_transient( $lock_key, 1, 10 * MINUTE_IN_SECONDS );
        }
        $batch = $this->manager->get_active_after_id(
            $product_id,
            $variation_id,
            25,
            $after_id
        );
        if ( empty( $batch ) ) {
            delete_transient( $lock_key );
            return;
        }
        $retention = (string) (Settings::get()['restock_subscriber_retention'] ?? 'keep');
        foreach ( $batch as $sub ) {
            if ( !$this->subscription_line_is_in_stock( $sub ) ) {
                continue;
            }
            if ( !$this->send_restock_email( $sub, 'remove' !== $retention ) ) {
                continue;
            }
            $this->manager->update_status( (int) $sub->id, 'notified' );
            if ( 'remove' === $retention ) {
                $this->manager->delete_subscription( (int) $sub->id, false );
            }
        }
        if ( count( $batch ) === 25 ) {
            $last = end( $batch );
            $next_after_id = ( is_object( $last ) ? (int) $last->id : $after_id );
            wp_schedule_single_event( time() + 30, 'stockbeacon_send_restock_batch', array($product_id, $variation_id, $next_after_id) );
        } else {
            delete_transient( $lock_key );
        }
    }

    public function send_restock_email( object $subscription, bool $schedule_reminder = true ) : bool {
        $product = wc_get_product( (int) $subscription->product_id );
        if ( !$product ) {
            return false;
        }
        $settings = Settings::get();
        $ctx = $this->get_restock_email_merge_context( $product );
        $subject = $this->apply_restock_merge_tags( (string) ($settings['restock_email_subject'] ?? '{product_name} is back in stock!'), $ctx );
        $heading = $this->apply_restock_merge_tags( (string) ($settings['restock_email_heading'] ?? "Great news - it's back!"), $ctx );
        $body_raw = $this->apply_restock_merge_tags( (string) ($settings['restock_email_body'] ?? ''), $ctx );
        $link = ( $product ? get_permalink( $product->get_id() ) : home_url( '/' ) );
        $token = $this->manager->issue_unsubscribe_token( (int) $subscription->id, (string) $subscription->email );
        if ( null === $token ) {
            return false;
        }
        set_transient( 'stockbeacon_unsubscribe_' . $token, array(
            'subscription_id' => (int) $subscription->id,
            'product_id'      => (int) $subscription->product_id,
            'variation_id'    => (int) ($subscription->variation_id ?? 0),
            'product_name'    => $product->get_name(),
            'product_url'     => (string) $link,
            'logged'          => false,
        ), 90 * DAY_IN_SECONDS );
        $body = $this->render_restock_email_template(
            $heading,
            $body_raw,
            (string) ($settings['restock_email_cta_text'] ?? 'Shop Now'),
            (string) $link,
            add_query_arg( 'stockbeacon_unsubscribe', $token, home_url( '/' ) ),
            '',
            $settings
        );
        $headers = array('Content-Type: text/html; charset=UTF-8');
        $reply_raw = trim( (string) ($settings['email_reply_to'] ?? '') );
        if ( is_email( $reply_raw ) ) {
            $headers[] = 'Reply-To: ' . $reply_raw;
        }
        $sent = $this->send_mail(
            (string) $subscription->email,
            $subject,
            $body,
            $headers
        );
        if ( $sent ) {
            $this->log_email_activity(
                (int) $subscription->id,
                'email_restock_sent',
                (string) $subscription->email,
                'restock',
                $subject,
                true,
                array(
                    'product_id'   => (int) $subscription->product_id,
                    'variation_id' => (int) ($subscription->variation_id ?? 0),
                )
            );
            do_action( 'stockbeacon_restock_email_sent', (int) $subscription->id, array(
                'email'        => (string) $subscription->email,
                'product_id'   => (int) $subscription->product_id,
                'variation_id' => (int) ($subscription->variation_id ?? 0),
            ) );
            if ( $schedule_reminder ) {
            }
            return true;
        }
        $this->log_email_activity(
            (int) $subscription->id,
            'email_restock_failed',
            (string) $subscription->email,
            'restock',
            $subject,
            false,
            array(
                'product_id'   => (int) $subscription->product_id,
                'variation_id' => (int) ($subscription->variation_id ?? 0),
            )
        );
        return false;
    }

    /**
     * Send a one-off restock-style email for admin testing (merge tags + sample product).
     *
     * @param array<string, string> $overrides Optional keys merged over settings: restock_email_subject, restock_email_heading, restock_email_body, restock_email_cta_text, plus email color and typography keys.
     * @return true|\WP_Error
     */
    public function send_test_restock_email( string $to, array $overrides = array() ) {
        $to = sanitize_email( $to );
        if ( !is_email( $to ) ) {
            return new \WP_Error('invalid_email', __( 'Invalid email address.', 'stockbeacon-product-waitlists-for-woocommerce' ), array(
                'status' => 400,
            ));
        }
        $allowed = array(
            'restock_email_subject',
            'restock_email_heading',
            'restock_email_body',
            'restock_email_cta_text',
            'email_accent_color',
            'email_heading_font_size',
            'email_body_font_size',
            'email_button_font_size',
            'email_footer_font_size',
            'email_box_shadow_enabled'
        );
        $settings = array_merge( Settings::get(), array_intersect_key( $overrides, array_flip( $allowed ) ) );
        $product = $this->get_sample_product_for_test_email();
        $ctx = $this->get_restock_email_merge_context( $product );
        $subject = $this->apply_restock_merge_tags( (string) ($settings['restock_email_subject'] ?? '{product_name} is back in stock!'), $ctx );
        $heading = $this->apply_restock_merge_tags( (string) ($settings['restock_email_heading'] ?? "Great news - it's back!"), $ctx );
        $body_raw = $this->apply_restock_merge_tags( (string) ($settings['restock_email_body'] ?? ''), $ctx );
        $link = ( $product ? get_permalink( $product->get_id() ) : home_url( '/' ) );
        $body = $this->render_restock_email_template(
            $heading,
            $body_raw,
            (string) ($settings['restock_email_cta_text'] ?? 'Shop Now'),
            (string) $link,
            null,
            esc_html__( 'This is a test email sent from StockBeacon in your WordPress admin. No waitlist subscription was created.', 'stockbeacon-product-waitlists-for-woocommerce' ),
            $settings
        );
        $headers = array('Content-Type: text/html; charset=UTF-8');
        $reply_raw = trim( (string) ($settings['email_reply_to'] ?? '') );
        if ( is_email( $reply_raw ) ) {
            $headers[] = 'Reply-To: ' . $reply_raw;
        }
        $product_meta = array(
            'product_name' => ( $product ? $product->get_name() : __( 'Sample product', 'stockbeacon-product-waitlists-for-woocommerce' ) ),
            'product_url'  => (string) $link,
        );
        $sent = $this->send_mail(
            $to,
            $subject,
            $body,
            $headers
        );
        if ( !$sent ) {
            $error_message = __( 'The test email could not be sent. Check your site mail configuration.', 'stockbeacon-product-waitlists-for-woocommerce' );
            $from_mailer = $this->get_last_mail_error_message();
            if ( '' !== $from_mailer ) {
                $error_message = sprintf( 
                    /* translators: %s: mailer failure reason */
                    __( 'The test email could not be sent: %s', 'stockbeacon-product-waitlists-for-woocommerce' ),
                    $from_mailer
                 );
            }
            $this->log_email_activity(
                0,
                'email_test_failed',
                $to,
                'restock',
                $subject,
                false,
                $product_meta
            );
            return new \WP_Error('send_failed', $error_message, array(
                'status' => 500,
            ));
        }
        $this->log_email_activity(
            0,
            'email_test_sent',
            $to,
            'restock',
            $subject,
            true,
            $product_meta
        );
        return true;
    }

    /**
     * @return \WC_Product|null
     */
    private function get_sample_product_for_test_email() {
        if ( !function_exists( 'wc_get_products' ) ) {
            return null;
        }
        $ids = wc_get_products( array(
            'limit'   => 1,
            'return'  => 'ids',
            'status'  => 'publish',
            'orderby' => 'modified',
            'order'   => 'DESC',
        ) );
        if ( empty( $ids ) ) {
            return null;
        }
        $p = wc_get_product( (int) $ids[0] );
        return ( $p instanceof \WC_Product ? $p : null );
    }

    /**
     * @param \WC_Product|null $product
     * @return array<string, string>
     */
    private function get_restock_email_merge_context( $product ) : array {
        $store_name = html_entity_decode( wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ), ENT_QUOTES, 'UTF-8' );
        if ( $product instanceof \WC_Product ) {
            $price_html = $product->get_price_html();
            $price_plain = ( $price_html ? trim( wp_strip_all_tags( html_entity_decode( $price_html, ENT_QUOTES | ENT_HTML5, 'UTF-8' ) ) ) : '' );
            return array(
                '{product_name}'  => $product->get_name(),
                '{product_url}'   => get_permalink( $product->get_id() ),
                '{product_price}' => ( '' !== $price_plain ? $price_plain : '—' ),
                '{store_name}'    => $store_name,
            );
        }
        return array(
            '{product_name}'  => __( 'Sample product', 'stockbeacon-product-waitlists-for-woocommerce' ),
            '{product_url}'   => home_url( '/' ),
            '{product_price}' => '—',
            '{store_name}'    => $store_name,
        );
    }

    /**
     * @param array<string, string> $ctx Keys like {product_name}
     */
    private function apply_restock_merge_tags( string $text, array $ctx ) : string {
        return str_replace( array_keys( $ctx ), array_values( $ctx ), $text );
    }

    /**
     * Resolve the colors, fonts, and card chrome used by the HTML email shell.
     *
     * @param array<string, mixed> $settings
     * @return array<string, string|int>
     */
    private function resolve_email_styles( array $settings ) : array {
        $accent = (string) ($settings['email_accent_color'] ?? $settings['form_accent_color'] ?? '#2563eb');
        $styles = array(
            'accent'       => ( sanitize_hex_color( $accent ) ?: '#2563eb' ),
            'bg'           => '#f1f5f9',
            'card_bg'      => '#ffffff',
            'card_border'  => '#e2e8f0',
            'card_radius'  => '10px',
            'card_shadow'  => '0 10px 30px rgba(15,23,42,.08)',
            'font'         => '-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif',
            'heading_size' => 30,
            'cta_radius'   => '8px',
            'cta_padding'  => '11px 16px',
        );
        return $styles;
    }

    /**
     * @param array<string, mixed> $settings Full settings array, plus any editor preview overrides.
     */
    private function render_restock_email_template(
        string $heading,
        string $body_raw,
        string $cta_text,
        string $cta_url,
        ?string $unsubscribe_url,
        string $footer_note,
        array $settings
    ) : string {
        $styles = $this->resolve_email_styles( $settings );
        $safe_heading = esc_html( $heading );
        $safe_body = wpautop( wp_kses_post( $body_raw ) );
        $safe_cta_text = esc_html( $cta_text );
        $safe_cta_url = esc_url( $cta_url );
        $safe_accent = (string) $styles['accent'];
        $safe_bg = (string) $styles['bg'];
        $safe_card_bg = (string) $styles['card_bg'];
        $safe_font = (string) $styles['font'];
        $heading_size = min( 48, max( 18, ( absint( $settings['email_heading_font_size'] ?? 30 ) ?: (int) $styles['heading_size'] ) ) );
        $body_size = min( 24, max( 12, ( absint( $settings['email_body_font_size'] ?? 16 ) ?: 16 ) ) );
        $button_size = min( 22, max( 12, ( absint( $settings['email_button_font_size'] ?? 15 ) ?: 15 ) ) );
        $footer_size = min( 16, max( 10, ( absint( $settings['email_footer_font_size'] ?? 12 ) ?: 12 ) ) );
        $card_shadow = ( filter_var( $settings['email_box_shadow_enabled'] ?? false, FILTER_VALIDATE_BOOLEAN ) ? (string) $styles['card_shadow'] : 'none' );
        $safe_note = ( '' !== $footer_note ? '<p style="margin:16px 0 0;font-size:' . esc_attr( (string) $footer_size ) . 'px;line-height:1.45;color:#787c82;">' . esc_html( $footer_note ) . '</p>' : '' );
        $unsubscribe = '';
        if ( $unsubscribe_url ) {
            $unsubscribe = '<p style="margin:14px 0 0;font-size:' . esc_attr( (string) $footer_size ) . 'px;line-height:1.45;"><a href="' . esc_url( $unsubscribe_url ) . '" style="color:#64748b;text-decoration:underline;">' . esc_html__( 'Unsubscribe', 'stockbeacon-product-waitlists-for-woocommerce' ) . '</a></p>';
        }
        return '' . '<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"></head><body style="margin:0;padding:24px;background:' . esc_attr( $safe_bg ) . ';color:#1f2937;font-family:' . esc_attr( $safe_font ) . ';">' . '<table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="border-collapse:collapse;"><tr><td align="center">' . '<table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="max-width:640px;border-collapse:collapse;">' . '<tr><td style="padding:24px;background:' . esc_attr( $safe_card_bg ) . ';border:1px solid ' . esc_attr( (string) $styles['card_border'] ) . ';border-radius:' . esc_attr( (string) $styles['card_radius'] ) . ';box-shadow:' . esc_attr( $card_shadow ) . ';">' . '<h1 style="margin:0 0 12px;font-size:' . esc_attr( (string) $heading_size ) . 'px;line-height:1.2;font-weight:700;color:#111827;">' . $safe_heading . '</h1>' . '<div style="margin:0 0 16px;font-size:' . esc_attr( (string) $body_size ) . 'px;line-height:1.65;color:#374151;">' . $safe_body . '</div>' . '<p style="margin:0;"><a href="' . $safe_cta_url . '" style="display:inline-block;background:' . esc_attr( $safe_accent ) . ';color:#ffffff !important;text-decoration:none;padding:' . esc_attr( (string) $styles['cta_padding'] ) . ';border-radius:' . esc_attr( (string) $styles['cta_radius'] ) . ';font-size:' . esc_attr( (string) $button_size ) . 'px;font-weight:700;">' . $safe_cta_text . '</a></p>' . $unsubscribe . $safe_note . '</td></tr>' . '</table>' . '</td></tr></table>' . '</body></html>';
    }

}
