<?php

namespace MailerPress\Actions\ActionScheduler\Processors;

use DI\DependencyException;
use DI\NotFoundException;
use MailerPress\Core\Attributes\Action;
use MailerPress\Core\DynamicPostRenderer;
use MailerPress\Core\Enums\Tables;
use MailerPress\Core\Interfaces\ContactFetcherInterface;
use MailerPress\Core\Kernel;
use MailerPress\Models\Contacts;
use MailerPress\Services\ClassicContactFetcher;
use MailerPress\Services\SegmentContactFetcher;

class MailerPressEmailBatch
{
    /**
     * @param $sendType
     * @param $post
     * @param $config
     * @param $scheduledAt
     * @param $recipientTargeting
     * @param $lists
     * @param $tags
     * @param $segment
     * @return void|\WP_REST_Response
     */
    #[Action('mailerpress_batch_email', priority: 10, acceptedArgs: 8)]
    public function process(
        $sendType = null,
        $post = null,
        $config = null,
        $scheduledAt = null,
        $recipientTargeting = null,
        $lists = null,
        $tags = null,
        $segment = null,
    ) {
        global $wpdb;

        try {
            error_log('MailerPress EmailBatch: process() called with ' . func_num_args() . ' arguments');

            // Handle case where ActionScheduler passes arguments as an array (first argument)
            // This happens when ActionScheduler calls do_action with array instead of spread
            if (is_array($sendType) && $post === null) {
                $args = $sendType;
                $sendType = $args[0] ?? null;
                $post = $args[1] ?? null;
                $config = $args[2] ?? null;
                $scheduledAt = $args[3] ?? null;
                $recipientTargeting = $args[4] ?? null;
                $lists = $args[5] ?? [];
                $tags = $args[6] ?? [];
                $segment = $args[7] ?? [];
            }

            // Ensure arrays are arrays
            $lists = is_array($lists) ? $lists : [];
            $tags = is_array($tags) ? $tags : [];
            $segment = is_array($segment) ? $segment : [];

            // Log raw data received
            error_log('MailerPress EmailBatch: Raw data received. lists type: ' . gettype($lists) . ', lists content: ' . var_export($lists, true));
            error_log('MailerPress EmailBatch: Raw data received. tags type: ' . gettype($tags) . ', tags content: ' . var_export($tags, true));

            // Validate essential parameters
            if (empty($post) || empty($config)) {
                error_log('MailerPress EmailBatch: Missing essential parameters. post: ' . var_export($post, true) . ', config: ' . var_export($config, true) . ', sendType: ' . var_export($sendType, true));
                return;
            }

            // Log for debugging (can be removed later)
            error_log('MailerPress EmailBatch: Processing campaign. post: ' . $post . ', sendType: ' . var_export($sendType, true) . ', recipientTargeting: ' . var_export($recipientTargeting, true));

            // Select fetcher based on targeting type
            // Default to 'classic' if recipientTargeting is null or empty
            $recipientTargeting = $recipientTargeting ?? 'classic';
            error_log('MailerPress EmailBatch: Getting fetcher. recipientTargeting: ' . $recipientTargeting . ', lists count: ' . count($lists) . ', tags count: ' . count($tags));
            $fetcher = $this->getFetcher($recipientTargeting, $lists, $tags, $segment);

            if (!$fetcher) {
                // Log error if fetcher is still null (should not happen with fallback)
                error_log('MailerPress EmailBatch ERROR: Failed to get contact fetcher. recipientTargeting: ' . var_export($recipientTargeting, true));
                return;
            }
            error_log('MailerPress EmailBatch: Fetcher obtained successfully');

            // Frequency settings
            $frequencySending = get_option('mailerpress_frequency_sending', [
                "settings" => [
                    "numberEmail" => 25,
                    "config" => ["value" => 5, "unit" => "minutes"],
                ],
            ]);

            if (is_string($frequencySending)) {
                $decoded = json_decode($frequencySending, true);
                $frequencySending = is_array($decoded) ? $decoded : [
                    "settings" => [
                        "numberEmail" => 25,
                        "config" => ["value" => 5, "unit" => "minutes"],
                    ],
                ];
            }

            $numberEmail = $frequencySending['settings']['numberEmail'] ?? 25;
            $frequencyConfig = $frequencySending['settings']['config'] ?? ['value' => 5, 'unit' => 'minutes'];

            $unit_multipliers = ['seconds' => 1, 'minutes' => MINUTE_IN_SECONDS, 'hours' => HOUR_IN_SECONDS];
            $interval_seconds = ($frequencyConfig['value'] ?? 5) * ($unit_multipliers[$frequencyConfig['unit']] ?? MINUTE_IN_SECONDS);

            $status = ('future' === $sendType) ? 'scheduled' : 'pending';

            // Get subject from config or fallback to campaign title
            $subject = $config['subject'] ?? '';
            if (empty($subject) && !empty($post)) {
                $campaign = get_post($post);
                $subject = $campaign ? $campaign->post_title : '';
            }

            // Check if a batch already exists for this campaign (created in createBatchV2)
            $existing_batch = $wpdb->get_row(
                $wpdb->prepare(
                    "SELECT id FROM " . Tables::get(Tables::MAILERPRESS_EMAIL_BATCHES) . " 
                 WHERE campaign_id = %d AND status IN ('scheduled', 'pending') 
                 ORDER BY id DESC LIMIT 1",
                    $post
                )
            );

            if ($existing_batch) {
                // Use existing batch
                $batch_id = (int)$existing_batch->id;
            } else {
                // Insert new batch record (fallback for old code paths)
                $wpdb->insert(
                    Tables::get(Tables::MAILERPRESS_EMAIL_BATCHES),
                    [
                        'status' => $status,
                        'total_emails' => 0,
                        'sender_name' => $config['fromName'] ?? '',
                        'sender_to' => $config['fromTo'] ?? '',
                        'subject' => $subject,
                        'scheduled_at' => $scheduledAt,
                        'campaign_id' => $post,
                    ]
                );

                $batch_id = $wpdb->insert_id;
                if (!$batch_id) {
                    return new \WP_REST_Response(null, 400);
                }
            }

            $htmlContent = get_option('mailerpress_batch_' . $post . '_html');
            error_log('MailerPress EmailBatch: HTML content retrieved. Length: ' . (is_string($htmlContent) ? strlen($htmlContent) : 'NULL') . ', Option key: mailerpress_batch_' . $post . '_html');

            if (empty($htmlContent)) {
                error_log('MailerPress EmailBatch ERROR: HTML content is empty! Option key: mailerpress_batch_' . $post . '_html');
                // Try to get HTML from campaign meta as fallback
                $campaign = get_post($post);
                if ($campaign) {
                    $meta = get_post_meta($post, 'meta', true);
                    if (is_string($meta)) {
                        $meta = json_decode($meta, true);
                    }
                    if (isset($meta['json'])) {
                        // This is a fallback - the HTML should have been saved by createBatchV2
                        error_log('MailerPress EmailBatch: HTML not found in option, but found in meta. This should not happen.');
                    }
                }
                // CRITICAL: Return early if HTML is missing - cannot send emails without content
                error_log('MailerPress EmailBatch CRITICAL ERROR: Cannot proceed without HTML content. Campaign ID: ' . $post);
                return;
            }

            if (!empty($htmlContent) && containsStartQueryBlock($htmlContent)) {
                error_log('MailerPress EmailBatch: HTML contains query block, rendering...');
                $renderer = new DynamicPostRenderer($htmlContent);
                $renderer->setCampaignId($post);
                $renderedHtml = $renderer->render();

                // Check if there are any new posts (not processed yet)
                if (empty($renderedHtml)) {
                    error_log('MailerPress EmailBatch: No new content to send for automated campaign');
                    // No new posts to send, stop the process
                    return new \WP_REST_Response(__('No new content to send for this automated campaign'), 400);
                }

                $htmlContent = $renderedHtml;
                error_log('MailerPress EmailBatch: Rendered HTML length: ' . strlen($htmlContent));
            }


            $dbChunk = 1000;
            $offset = 0;
            $chunk_index = 0;
            $totalEmails = 0;
            $now = time();

            // Calculate base time for scheduling chunks
            $base_time = $now;
            if ('future' === $sendType && !empty($scheduledAt)) {
                $base_time = $this->convert_scheduled_at_to_timestamp($scheduledAt);
                if ($base_time <= $now) {
                    // If scheduled time is past, fallback to now
                    $base_time = $now;
                }
            }

            $foundContacts = false;

            $servicesData = get_option('mailerpress_email_services', []);
            $defaultService = $servicesData['default_service'] ?? '';
            error_log('MailerPress EmailBatch: Default service: ' . $defaultService);

            // Get API key from service configuration
            $apiKey = '';
            if (!empty($defaultService) && isset($servicesData['services'][$defaultService]['conf']['api_key'])) {
                $apiKey = $servicesData['services'][$defaultService]['conf']['api_key'];
            } elseif (isset($config['api_key'])) {
                $apiKey = $config['api_key'];
            }

            if ('mailerpress' === $defaultService) {
                error_log('MailerPress EmailBatch: Using mailerpress service, fetching contacts...');
                $offset = 0;
                $foundContacts = false;
                $totalEmails = 0;

                do {
                    $contacts = $fetcher->fetch($dbChunk, $offset);
                    error_log('MailerPress EmailBatch: Fetched ' . count($contacts) . ' contacts at offset ' . $offset);
                    if (empty($contacts)) {
                        break;
                    }

                    $foundContacts = true;
                    $totalEmails += count($contacts);

                    // Split contacts into smaller chunks for Laravel
                    $sendingChunks = array_chunk($contacts, 1000);

                    foreach ($sendingChunks as $sendingChunk) {



                        $payload = [
                            'wp_batch_id' => $batch_id,
                            'domain_id' => $servicesData['services']['mailerpress']['conf']['domain'],
                            'name' => $subject,
                            'emails' => array_map(function ($c) use ($htmlContent, $subject) {
                                $contact = Kernel::getContainer()->get(Contacts::class)->get($c);
                                return [
                                    'to' => $contact->email,
                                    'subject' => $subject,
                                    'body' => $htmlContent,
                                ];
                            }, $sendingChunk),
                        ];

                        $payloadJson = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

                        // Send to Laravel API
                        $response = wp_remote_post('https://mailerpress.pro/api/batch', [
                            'headers' => [
                                'Content-Type' => 'application/json',
                                'x-api-key' => $servicesData['services']['mailerpress']['conf']['api_key'],
                            ],
                            'body' => $payloadJson,
                            'timeout' => 60,
                        ]);

                        if (is_wp_error($response)) {
                            // Mark batch as failed if API error occurs
                            $this->markBatchAsFailed($batch_id, $post, 'Laravel API error: ' . $response->get_error_message());
                            return;
                        } else {
                            $status_code = wp_remote_retrieve_response_code($response);
                            $body = wp_remote_retrieve_body($response);
                        }
                    }

                    $offset += $dbChunk;
                } while (!empty($contacts));

                // Update batch totals
                error_log('MailerPress EmailBatch: Finished fetching. foundContacts: ' . ($foundContacts ? 'true' : 'false') . ', totalEmails: ' . $totalEmails);
                if (!$foundContacts) {
                    error_log('MailerPress EmailBatch ERROR: No contacts found!');
                    $wpdb->update(
                        Tables::get(Tables::MAILERPRESS_CAMPAIGNS),
                        ['status' => 'error', 'updated_at' => current_time('mysql')],
                        ['campaign_id' => intval($post)],
                        ['%s', '%s'],
                        ['%d']
                    );
                } else {
                    error_log('MailerPress EmailBatch: Updating batch with ' . $totalEmails . ' emails');
                    $wpdb->update(
                        Tables::get(Tables::MAILERPRESS_EMAIL_BATCHES),
                        ['total_emails' => $totalEmails],
                        ['id' => $batch_id],
                        ['%d'],
                        ['%d']
                    );
                    do_action('mailerpress_batch_event', $status, $post, $batch_id);
                }
            } else {
                error_log('MailerPress EmailBatch: Using other service, fetching contacts...');
                do {
                    $contacts = $fetcher->fetch($dbChunk, $offset);
                    error_log('MailerPress EmailBatch: Fetched ' . count($contacts) . ' contacts at offset ' . $offset);
                    if (empty($contacts)) {
                        break;
                    }

                    $foundContacts = true;
                    $totalEmails += count($contacts);
                    error_log('MailerPress EmailBatch: Found contacts! Total so far: ' . $totalEmails);
                    $sendingChunks = array_chunk($contacts, $numberEmail);

                    foreach ($sendingChunks as $sendingChunk) {
                        $transient_key = 'mailerpress_chunk_' . $batch_id . '_' . $chunk_index;
                        set_transient($transient_key, [
                            'html' => $htmlContent,
                            'campaignId' => $post,
                            'subject' => $config['subject'],
                            'sender_name' => $config['fromName'],
                            'api_key' => $apiKey,
                            'sender_to' => $config['fromTo'],
                            'contacts' => $sendingChunk,
                            'scheduled_at' => $scheduledAt,
                            'webhook_url' => get_rest_url(null, 'mailerpress/v1/webhook/notify'),
                            'sendType' => $sendType,
                        ]);

                        $scheduled_time = $base_time + ($chunk_index * $interval_seconds);

                        as_schedule_single_action(
                            $scheduled_time,
                            'mailerpress_process_contact_chunk',
                            [$batch_id, $transient_key],
                            'mailerpress'
                        );

                        $chunk_index++;
                    }

                    $offset += $dbChunk;
                } while (!empty($contacts));

                error_log('MailerPress EmailBatch: Finished fetching. foundContacts: ' . ($foundContacts ? 'true' : 'false') . ', totalEmails: ' . $totalEmails);
                if (!$foundContacts) {
                    error_log('MailerPress EmailBatch ERROR: No contacts found!');
                    $wpdb->update(
                        Tables::get(Tables::MAILERPRESS_CAMPAIGNS),
                        ['status' => 'error', 'updated_at' => current_time('mysql')],
                        ['campaign_id' => intval($post)],
                        ['%s', '%s'],
                        ['%d']
                    );
                }

                // Update batch totals or mark error
                if ($totalEmails > 0) {
                    error_log('MailerPress EmailBatch: Updating batch with ' . $totalEmails . ' emails');
                    $wpdb->update(
                        Tables::get(Tables::MAILERPRESS_EMAIL_BATCHES),
                        ['total_emails' => $totalEmails],
                        ['id' => $batch_id],
                        ['%d'],
                        ['%d']
                    );
                    do_action('mailerpress_batch_event', $status, $post, $batch_id);
                } else {
                    error_log('MailerPress EmailBatch ERROR: totalEmails is 0, marking campaign as error');
                    $wpdb->update(
                        Tables::get(Tables::MAILERPRESS_CAMPAIGNS),
                        ['status' => 'error', 'updated_at' => current_time('mysql')],
                        ['campaign_id' => intval($post)],
                        ['%s', '%s'],
                        ['%d']
                    );
                }
            }

            error_log('MailerPress EmailBatch: process() completed successfully for campaign ' . $post);
        } catch (\Throwable $e) {
            error_log('MailerPress EmailBatch CRITICAL ERROR: ' . $e->getMessage());
            error_log('MailerPress EmailBatch CRITICAL ERROR: Stack trace: ' . $e->getTraceAsString());
            error_log('MailerPress EmailBatch CRITICAL ERROR: File: ' . $e->getFile() . ', Line: ' . $e->getLine());

            // Try to mark batch as failed if we have the batch_id
            if (isset($batch_id) && !empty($batch_id) && !empty($post)) {
                try {
                    $this->markBatchAsFailed($batch_id, $post, 'Critical error: ' . $e->getMessage());
                } catch (\Throwable $e2) {
                    error_log('MailerPress EmailBatch: Failed to mark batch as failed: ' . $e2->getMessage());
                }
            } elseif (!empty($post)) {
                // Try to find batch_id from database
                global $wpdb;
                $batch = $wpdb->get_row($wpdb->prepare(
                    "SELECT id FROM " . Tables::get(Tables::MAILERPRESS_EMAIL_BATCHES) . " 
                     WHERE campaign_id = %d 
                     ORDER BY id DESC LIMIT 1",
                    $post
                ));
                if ($batch) {
                    try {
                        $this->markBatchAsFailed((int)$batch->id, $post, 'Critical error: ' . $e->getMessage());
                    } catch (\Throwable $e2) {
                        error_log('MailerPress EmailBatch: Failed to mark batch as failed: ' . $e2->getMessage());
                    }
                }
            }
        }
    }

    /**
     * Convert scheduled_at string (WP timezone) to Unix timestamp.
     */
    private function convert_scheduled_at_to_timestamp(string $scheduledAt): int
    {
        $tz = function_exists('wp_timezone') ? wp_timezone() : new \DateTimeZone(wp_timezone_string());
        try {
            $dt = new \DateTime($scheduledAt, $tz);
            return $dt->getTimestamp();
        } catch (\Exception $e) {
            // fallback to current time if parsing fails
            return time();
        }
    }

    /**
     * Mark batch and campaign as failed
     */
    private function markBatchAsFailed(int $batch_id, int $campaign_id, string $error_message): void
    {
        global $wpdb;

        $batchTable = Tables::get(Tables::MAILERPRESS_EMAIL_BATCHES);
        $campaignTable = Tables::get(Tables::MAILERPRESS_CAMPAIGNS);

        // Update batch status to failed
        $wpdb->update(
            $batchTable,
            [
                'status' => 'failed',
                'updated_at' => current_time('mysql'),
            ],
            ['id' => $batch_id],
            ['%s', '%s'],
            ['%d']
        );

        // Update campaign status to error
        $wpdb->update(
            $campaignTable,
            [
                'status' => 'error',
                'updated_at' => current_time('mysql'),
            ],
            ['campaign_id' => $campaign_id],
            ['%s', '%s'],
            ['%d']
        );
    }

    /**
     * Returns a ContactFetcher based on recipient targeting
     */
    private function getFetcher(
        ?string $type,
        array $lists,
        array $tags,
        $segment
    ): ?ContactFetcherInterface {
        // Default to 'classic' if type is null or empty (backward compatibility)
        $type = $type ?? 'classic';

        return match ($type) {
            'classic' => new ClassicContactFetcher($lists, $tags),
            'segment' => new SegmentContactFetcher(is_array($segment) ? $segment[0] : $segment),
            default => new ClassicContactFetcher($lists, $tags) // Fallback to classic instead of null
        };
    }
}
