<?php

namespace AIPT\Api\AutomationJobs\Services;

use AIPT\Api\AutomationJobs\DraftContentManager;
use AIPT\Api\AutomationJobs\Interfaces\JobLoggerInterface;

/**
 * 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 {
    /** @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'        => [],
                    '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;
            }

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

        return array_values($grouped);
    }

    /**
     * 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 );
        }
    }

    /**
     * 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;
        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]; }
        }

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

    private function applyToProduct(array $result): 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;
        }

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

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

        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');
        }
    }
}

