<?php

namespace AIPT\Api\AutomationJobs\Services;

use AIPT\Api\AutomationJobs\DraftContentManager;
use AIPT\Api\AutomationJobs\Interfaces\JobLoggerInterface;
use AIPT\Api\BulkGenerator\SeoMeta\Core\SeoPluginDetector;
use AIPT\Api\BulkGenerator\SeoMeta\Core\SeoMetaWriter;

/**
 * ResultProcessor
 *
 * Encapsulates transformation and persistence of AI results.
 * Applies results to draft system or directly to WooCommerce products.
 * Provides batched failure updates for throughput under errors.
 *
 * @since 2.3.0
 */
class ResultProcessor {
    public const NO_APPLICABLE_CONTENT_MESSAGE = 'No generated content was extracted from the AI response.';

    /** @var \wpdb */
    private $wpdb;

    /** @var string */
    private $job_products_table;

    /** @var JobLoggerInterface */
    private $logger;

    public function __construct(JobLoggerInterface $logger) {
        global $wpdb;
        $this->wpdb = $wpdb;
        $this->job_products_table = $wpdb->prefix . 'aipt_job_products';
        $this->logger = $logger;
    }

    /**
     * Transform raw tool results to a unified structure suitable for persistence.
     * Input format depends on tool; output is normalized per product id.
     *
     * @param int   $job_id
     * @param array $raw_results
     * @return array[] Array of per-product result arrays
     */
    public function transformToUnifiedFormat(int $job_id, array $raw_results): array {
        $grouped = [];

        foreach ($raw_results as $item) {
            if (!is_array($item)) { continue; }
            $product_id = isset($item['product_id']) ? (int) $item['product_id'] : 0;
            if ($product_id <= 0) { continue; }

            if (!isset($grouped[$product_id])) {
                $grouped[$product_id] = [
                    'job_id'      => $job_id,
                    'product_id'  => $product_id,
                    'title'       => null,
                    'description' => null,
                    'short_desc'  => null,
                    'tags'        => [],
                    'faq'         => null,
                    'seo_meta'    => null,
                    'image_alt'   => [],
                    'raw'         => [],
                ];
            }

            $type = isset($item['type']) ? sanitize_text_field((string) $item['type']) : '';
            $text = isset($item['text']) ? (string) $item['text'] : null;

            if ($grouped[$product_id]['title'] === null) {
                $title = isset($item['title']) ? (string) $item['title'] : null;
                if ($title === null && $type === 'title') {
                    if (!empty($text)) {
                        $title = $text;
                    } elseif (!empty($item['variants']) && is_array($item['variants'])) {
                        $first = reset($item['variants']);
                        if (is_string($first) && $first !== '') { $title = $first; }
                    }
                }
                if (!empty($title)) { $grouped[$product_id]['title'] = $title; }
            }

            if ($grouped[$product_id]['description'] === null) {
                $description = isset($item['description']) ? (string) $item['description'] : null;
                if ($description === null && $type === 'full' && !empty($text)) { $description = $text; }
                if (!empty($description)) { $grouped[$product_id]['description'] = $description; }
            }
            if ($grouped[$product_id]['short_desc'] === null) {
                $short_desc = isset($item['short_description']) ? (string) $item['short_description'] : null;
                if ($short_desc === null && $type === 'short' && !empty($text)) { $short_desc = $text; }
                if (!empty($short_desc)) { $grouped[$product_id]['short_desc'] = $short_desc; }
            }

            $tags_arr = [];
            if (isset($item['tags']) && is_array($item['tags'])) {
                $tags_arr = array_values(array_filter(array_map('strval', $item['tags'])));
            } elseif ($type === 'tags' && is_string($text)) {
                $parts = array_map('trim', explode(',', $text));
                $tags_arr = array_values(array_filter($parts, function($v){ return $v !== ''; }));
            }
            if (!empty($tags_arr)) {
                $existing = $grouped[$product_id]['tags'];
                $merged = array_values(array_unique(array_merge($existing, $tags_arr)));
                $grouped[$product_id]['tags'] = $merged;
            }

            if ($grouped[$product_id]['faq'] === null && !empty($item['faqs']) && is_array($item['faqs'])) {
                $faqs = $this->normalizeFaqs($item['faqs']);
                if (!empty($faqs)) { $grouped[$product_id]['faq'] = $faqs; }
            }

            if ($grouped[$product_id]['seo_meta'] === null) {
                $seo_meta = [];
                if (isset($item['meta_title']) && is_string($item['meta_title'])) {
                    $meta_title = trim(sanitize_text_field($item['meta_title']));
                    if ($meta_title !== '') { $seo_meta['meta_title'] = $meta_title; }
                }
                if (isset($item['meta_description']) && is_string($item['meta_description'])) {
                    $meta_description = trim(sanitize_textarea_field($item['meta_description']));
                    if ($meta_description !== '') { $seo_meta['meta_description'] = $meta_description; }
                }
                if (isset($item['focus_keyword']) && is_string($item['focus_keyword'])) {
                    $focus_keyword = trim(sanitize_text_field($item['focus_keyword']));
                    if ($focus_keyword !== '') { $seo_meta['focus_keyword'] = $focus_keyword; }
                }
                if (!empty($seo_meta)) { $grouped[$product_id]['seo_meta'] = $seo_meta; }
            }

            if (isset($item['attachment_id'], $item['alt_text'])) {
                $status = isset($item['status']) ? sanitize_text_field((string) $item['status']) : '';
                if ($status !== 'error' && $status !== 'rejected') {
                    $attachment_id = (int) $item['attachment_id'];
                    $alt_text = trim(sanitize_text_field((string) $item['alt_text']));
                    if ($attachment_id > 0 && $alt_text !== '') {
                        $grouped[$product_id]['image_alt'][] = [
                            'attachment_id' => $attachment_id,
                            'alt_text'      => $alt_text,
                            'status'        => $status,
                        ];
                    }
                }
            }

            $grouped[$product_id]['raw'][] = $item;
        }

        return array_values($grouped);
    }

    /**
     * Check whether a normalized result contains content that can be applied.
     *
     * @param array $result Normalized result payload.
     * @return bool True when at least one applicable content field is present.
     */
    public function hasApplicableContent(array $result): bool {
        foreach (['title', 'description', 'short_desc'] as $field) {
            if (isset($result[$field]) && is_string($result[$field]) && trim($result[$field]) !== '') {
                return true;
            }
        }

        foreach (['tags', 'faq'] as $field) {
            if (!empty($result[$field]) && is_array($result[$field]) && $this->arrayHasNonEmptyValue($result[$field])) {
                return true;
            }
        }

        if (!empty($result['seo_meta']) && is_array($result['seo_meta'])) {
            foreach ($result['seo_meta'] as $value) {
                if (is_string($value) && trim($value) !== '') {
                    return true;
                }
            }
        }

        if (!empty($result['image_alt']) && is_array($result['image_alt'])) {
            foreach ($result['image_alt'] as $item) {
                if (is_array($item) && isset($item['alt_text']) && trim((string) $item['alt_text']) !== '') {
                    return true;
                }
            }
        }

        return false;
    }

    private function arrayHasNonEmptyValue(array $items): bool {
        foreach ($items as $item) {
            if (is_array($item)) {
                if ($this->arrayHasNonEmptyValue($item)) {
                    return true;
                }
                continue;
            }

            if (trim((string) $item) !== '') {
                return true;
            }
        }

        return false;
    }

    /**
     * Apply a single result either to drafts or directly to product.
     *
     * @param array               $result Normalized result
     * @param array               $content_settings Job content settings
     * @param DraftContentManager $draft_manager
     */
    public function applyResult(array $result, array $content_settings, DraftContentManager $draft_manager): void {

        $raw = isset($content_settings['status']) ? $content_settings['status'] : 'published';
        $status = strtolower(sanitize_text_field((string) $raw));

        if ($status === 'draft') {

            $this->saveDraft($result, $draft_manager);
        } else {

            $backup = ! empty( $content_settings['backupOriginal'] );

            /**
             * Action hook before applying automation result to product.
             *
             * Premium module can hook here to perform backup of original content
             * before the new content is applied.
             *
             * @since 2.5.0
             * @param array               $result        The result data to be applied.
             * @param DraftContentManager $draft_manager Draft content manager instance.
             * @param object              $logger        Logger instance.
             * @param bool                $backup        Whether backup was requested.
             */
            do_action( 'aipt_before_apply_result', $result, $draft_manager, $this->logger, $backup );

            $this->applyToProduct( $result, $content_settings );
        }
    }

    /**
     * Persist success for single product with result payload.
     */
    public function recordSuccess(int $job_id, int $product_id, array $result_payload): void {
        $this->wpdb->update(
            $this->job_products_table,
            [
                'status'       => 'completed',
                'result'       => wp_json_encode($result_payload),
                'processed_at' => current_time('mysql'),
                'error_message'=> null,
            ],
            [ 'job_id' => $job_id, 'product_id' => $product_id ],
            [ '%s', '%s', '%s' ],
            [ '%d', '%d' ]
        );
    }

    /**
     * Persist failure for a single product.
     */
    public function recordFailure(int $job_id, int $product_id, string $message): void {
        $this->wpdb->update(
            $this->job_products_table,
            [
                'status'        => 'failed',
                'error_message' => $message,
                'processed_at'  => current_time('mysql'),
            ],
            [ 'job_id' => $job_id, 'product_id' => $product_id ],
            [ '%s', '%s', '%s' ],
            [ '%d', '%d' ]
        );
    }

    /**
     * Persist failure for a batch of products with one message using single UPDATE.
     */
    public function recordFailuresBulk(int $job_id, array $product_ids, string $message): void {
        $ids = array_map('intval', array_filter($product_ids));
        if (empty($ids)) { return; }
        $ids_in = implode(',', $ids);

        $sql = $this->wpdb->prepare(
            "UPDATE {$this->job_products_table} SET status = %s, error_message = %s WHERE job_id = %d AND product_id IN ($ids_in)",
            ['failed', $message, (int) $job_id]
        );
        $res = $this->wpdb->query($sql);

        if ($res === false) {
            $this->logger->error($job_id, 'Bulk failure update DB error', [
                'type' => 'DB',
                'error' => $this->wpdb->last_error,
                'affected_products' => $ids_in
            ]);
        }
    }

    private function saveDraft(array $result, DraftContentManager $draft_manager): void {
        $job_id = isset($result['job_id']) ? (int)$result['job_id'] : 0;
        $product_id = isset($result['product_id']) ? (int)$result['product_id'] : 0;
        $created_by = isset($result['created_by']) ? (int) $result['created_by'] : null;
        if ($job_id <= 0 || $product_id <= 0) { return; }

        $pieces = [];
        if (!empty($result['title'])) { $pieces[] = ['type'=>'title','content'=>$result['title']]; }
        if (!empty($result['description'])) { $pieces[] = ['type'=>'description','content'=>$result['description']]; }
        if (!empty($result['short_desc'])) { $pieces[] = ['type'=>'short_description','content'=>$result['short_desc']]; }
        if (!empty($result['tags']) && is_array($result['tags'])) {
            $tag_str = implode(', ', array_filter(array_map('strval', $result['tags'])));
            if ($tag_str !== '') { $pieces[] = ['type'=>'tags','content'=>$tag_str]; }
        }
        if (!empty($result['faq']) && is_array($result['faq'])) {
            $content = wp_json_encode($result['faq']);
            if ($content !== false) { $pieces[] = ['type'=>'faq','content'=>$content]; }
        }
        if (!empty($result['seo_meta']) && is_array($result['seo_meta'])) {
            $content = wp_json_encode($result['seo_meta']);
            if ($content !== false) { $pieces[] = ['type'=>'seo_meta','content'=>$content]; }
        }
        if (!empty($result['image_alt']) && is_array($result['image_alt'])) {
            $content = wp_json_encode($result['image_alt']);
            if ($content !== false) { $pieces[] = ['type'=>'image_alt','content'=>$content]; }
        }

		foreach ($pieces as $p) {
			try {
				$draft_id = $draft_manager->saveDraftContent($job_id, $product_id, $p['type'], $p['content'], null, $created_by);
			} catch (\Throwable $e) {
				$this->logger->warning($job_id, 'Draft save failed', ['product_id'=>$product_id,'content_type'=>$p['type'],'error'=>$e->getMessage(), 'type' => 'DB']);

				throw new \Exception('Draft save failed for content type: ' . esc_html((string) $p['type']) . ': ' . esc_html($e->getMessage()), (int)($e->getCode() ?: 0), $e);
			}

			if (!$draft_id) {
				$this->logger->warning($job_id, 'Draft save returned false', ['product_id'=>$product_id,'content_type'=>$p['type'], 'type' => 'DB']);
				throw new \Exception('Draft save failed for content type: ' . esc_html((string) $p['type']));
			}
		}
	}

    private function applyToProduct(array $result, array $content_settings): void {
        $product_id = (int) $result['product_id'];
        if ($product_id <= 0) { return; }

        $product = function_exists('wc_get_product') ? wc_get_product($product_id) : null;
        if (!$product) {
            $this->logger->warning((int) $result['job_id'], 'Product not found for apply', [ 'product_id' => $product_id, 'type' => 'WC_UPDATE' ]);
            return;
        }

        $should_save_product = false;

        if (!empty($result['title'])) {
            $product->set_name($result['title']);
            $should_save_product = true;
        }
        if (!empty($result['description'])) {
            $product->set_description($result['description']);
            $should_save_product = true;
        }
        if (!empty($result['short_desc'])) {
            $product->set_short_description($result['short_desc']);
            $should_save_product = true;
        }

        if (!empty($result['tags']) && is_array($result['tags'])) {
            $this->applyTags($product_id, $result['tags']);
        }

        if (!empty($result['faq']) && is_array($result['faq'])) {
            $write_mode = isset($content_settings['writeMode']) ? sanitize_text_field((string) $content_settings['writeMode']) : 'append';
            $this->applyFaq($product_id, $result['faq'], $write_mode);
        }

        if (!empty($result['seo_meta']) && is_array($result['seo_meta'])) {
            $this->applySeoMeta($product_id, $result['seo_meta']);
        }

        if (!empty($result['image_alt']) && is_array($result['image_alt'])) {
            $this->applyImageAlt($product_id, $result['image_alt']);
        }

        if ($should_save_product) {
            try {
                $product->save();
            } catch (\Throwable $e) {
                $this->logger->error((int) $result['job_id'], 'Product save failed', [ 'product_id' => $product_id, 'error' => $e->getMessage(), 'type' => 'WC_UPDATE' ]);

                throw new \Exception('Product save failed for product ' . $product_id . ': ' . $e->getMessage(), (int)($e->getCode() ?: 0), $e);
            }
        }
    }

    private function applyTags(int $product_id, array $tag_names): void {
        $tag_ids = [];
        foreach ($tag_names as $name) {
            $name = trim((string) $name);
            if ($name === '') { continue; }
            $term = get_term_by('name', $name, 'product_tag');
            if ($term && !is_wp_error($term)) {
                $tag_ids[] = (int) $term->term_id;
                continue;
            }
            $created = wp_insert_term($name, 'product_tag');
            if (!is_wp_error($created) && isset($created['term_id'])) {
                $tag_ids[] = (int) $created['term_id'];
            }
        }
        if (!empty($tag_ids)) {
            wp_set_post_terms($product_id, $tag_ids, 'product_tag');
        }
    }

    private function normalizeFaqs(array $items): array {
        $faqs = [];
        foreach ($items as $faq) {
            if (!is_array($faq)) { continue; }
            $question = sanitize_text_field((string) ($faq['question'] ?? ''));
            $answer = wp_kses_post((string) ($faq['answer'] ?? ''));
            if ($question !== '' && $answer !== '') {
                $faqs[] = [
                    'question' => $question,
                    'answer'   => $answer,
                ];
            }
        }
        return $faqs;
    }

    private function applyFaq(int $product_id, array $faqs, string $write_mode): void {
        $structured_faqs = $this->normalizeFaqs($faqs);
        if ($write_mode === 'append') {
            $existing_json = get_post_meta($product_id, '_aipt_faq_data', true);
            $existing_faqs = !empty($existing_json) ? json_decode($existing_json, true) : [];
            if (is_array($existing_faqs)) {
                $structured_faqs = array_merge($existing_faqs, $structured_faqs);
            }
        }

        $json = wp_json_encode($structured_faqs);
        if ($json === false) {
            throw new \Exception('Failed to encode FAQ data for product ' . $product_id); 
        }
        update_post_meta($product_id, '_aipt_faq_data', wp_slash($json));
    }

    private function applySeoMeta(int $product_id, array $seo_meta): void {
        $detected_plugin = SeoPluginDetector::detect();
        if (null === $detected_plugin || empty($detected_plugin['plugin_slug'])) {
            throw new \Exception('No supported SEO plugin detected');
        }

        $write_data = SeoMetaWriter::buildWriteData($seo_meta, (string) $detected_plugin['plugin_slug']);
        if (empty($write_data)) { return; }

        $writer = new SeoMetaWriter();
        $results = $writer->bulkWrite($product_id, $write_data, (string) $detected_plugin['plugin_slug']);
        foreach (array_keys($write_data) as $key) {
            if (empty($results[$key])) {
                throw new \Exception('Failed to write SEO meta field: ' . $key); 
            }
        }
    }

    private function applyImageAlt(int $product_id, array $items): void {
        foreach ($items as $item) {
            if (!is_array($item)) { continue; }
            $attachment_id = isset($item['attachment_id']) ? (int) $item['attachment_id'] : 0;
            if ($attachment_id <= 0 || !$this->attachmentBelongsToProduct($attachment_id, $product_id)) { continue; }
            $alt_text = sanitize_text_field((string) ($item['alt_text'] ?? ''));
            $current_alt = (string) get_post_meta($attachment_id, '_wp_attachment_image_alt', true);
            if ($current_alt !== $alt_text) {
                update_post_meta($attachment_id, '_wp_attachment_image_alt', wp_slash($alt_text));
            }
        }
    }

    private function attachmentBelongsToProduct(int $attachment_id, int $product_id): bool {
        $product = function_exists('wc_get_product') ? wc_get_product($product_id) : null;
        if (!$product || !wp_get_attachment_image_url($attachment_id, 'full')) { return false; }
        if ((int) $product->get_image_id() === $attachment_id) { return true; }
        $gallery_ids = array_map('absint', (array) $product->get_gallery_image_ids());
        return in_array($attachment_id, $gallery_ids, true);
    }
}
