<?php

namespace AC_SYNC\Includes\Classes\Differential\Entities;

if (!defined('ABSPATH'))
    exit; // Exit if accessed directly
use AC_SYNC\Includes\Classes\Differential\Entities\Action_Wear_Entities as Entities;
use AC_SYNC\Includes\Classes\Differential\Entities\Action_Wear_Entities_Interface as EntitiesInterface;
use AC_SYNC\Includes\Classes\Differential\Schema\Action_Wear_Price_Schema as PriceSchema;
use AC_SYNC\Includes\Classes\Api\Action_Wear_Api as Api;
use AC_SYNC\Includes\Classes\Logger\Action_Wear_Log as Log;
use AC_SYNC\Includes\Classes\Product\Action_Wear_Product as Product;
use AC_SYNC\Includes\Classes\Price\Action_Wear_Price_Recharger as PriceRecharger;

class Action_Wear_Price extends Entities implements EntitiesInterface
{

    const DRY_RUN = false;
    const UPDATE_TIMING = 7200;

    public function getUpdateTiming(): int
    {
        return self::UPDATE_TIMING;
    }

    public function execute(): bool
    {
        if (get_option('_ACTIONWEAR_IS_PRICE_SYNC_DISABLED') == 1)
            return false;

        $date = date("Y-m-d H:i", $this->getExecutionTime());

        $prices = Api::external(
            "GET",
            "/pricesall?compress=0&output=json&from=$date"
        );

        $code = $prices["code"];
        if ($code !== 200) {
            $error_msg = "Errore durante la chiamata al differenziale dei prezzi, restituito codice: " . $code;
            Log::write($error_msg, Log::ERROR, Log::CONTEXT_CAMAC_API);
            throw new \Exception($error_msg);
        }

        $decoded = json_decode($prices["raw"]["body"], true);

        $prices = self::normalizeApiResponse($decoded);
        if ($prices === null || count($prices) === 0) {
            $this->updateExecutionTime();
            Log::write("Nessun aggiornamento di prezzo trovato nell'arco di tempo che va da $date ad adesso", Log::INFO, Log::CONTEXT_CAMAC_API);
            return false;
        }

        $first_key = array_key_first($prices);
        $firstItem = $prices[$first_key];
        if (!is_array($firstItem)) {
            Log::write("Schema prezzi non valido: formato risposta API inatteso. Primo elemento: " . gettype($firstItem), Log::ERROR, Log::CONTEXT_CAMAC_API);
            return false;
        }

        $this->setApiSchema(array_keys($firstItem));

        if (!$this->conformToSchema(new PriceSchema)) {
            $error_msg = "Lo schema dei prezzi ricevuti non è conforme a quello atteso";
            Log::write($error_msg, Log::ERROR, Log::CONTEXT_CAMAC_API);
            return false;
        }

        // Collect all SKUs first for batch lookup
        $allSimpleSkus = [];
        $allConfSkus = [];
        foreach ($prices as $price) {
            $skus = is_array($price["sku"]) ? $price["sku"] : [$price["sku"]];
            $allConfSkus[] = $price["sku_camac"];
            foreach ($skus as $sku) {
                if ($sku) $allSimpleSkus[] = $sku;
            }
        }
        $allSkusToLookup = array_unique(array_merge($allSimpleSkus, $allConfSkus));

        // Batch SKU -> post_id lookup (single query)
        global $wpdb;
        $skuToPostId = [];
        if (!empty($allSkusToLookup)) {
            $placeholders = implode(',', array_fill(0, count($allSkusToLookup), '%s'));
            $results = $wpdb->get_results(
                $wpdb->prepare(
                    "SELECT pm.meta_value AS sku, pm.post_id
                    FROM {$wpdb->postmeta} pm
                    INNER JOIN {$wpdb->posts} p ON p.ID = pm.post_id
                    WHERE pm.meta_key = '_sku'
                    AND pm.meta_value IN ($placeholders)",
                    ...$allSkusToLookup
                )
            );
            foreach ($results as $row) {
                $skuToPostId[$row->sku] = (int) $row->post_id;
            }
        }

        // Batch pre-fetch brand_id and cat_ids for price recharge
        $postIds = array_values($skuToPostId);
        $metaMap = [];
        if (!empty($postIds)) {
            $idPlaceholders = implode(',', array_fill(0, count($postIds), '%d'));
            $metaResults = $wpdb->get_results(
                $wpdb->prepare(
                    "SELECT post_id, meta_key, meta_value
                    FROM {$wpdb->postmeta}
                    WHERE post_id IN ($idPlaceholders)
                    AND meta_key IN ('brand_id', 'cat_ids')",
                    ...$postIds
                )
            );
            foreach ($metaResults as $meta) {
                $metaMap[$meta->post_id][$meta->meta_key] = $meta->meta_value;
            }
        }

        $processed = 0;
        foreach ($prices as $price) {
            $skus = is_array($price["sku"]) ? $price["sku"] : [$price["sku"]];
            $sku_conf = $price["sku_camac"];
            $prezzo = (float) str_replace(",", ".", $price["prezzo unita"]);

            $id_conf = $skuToPostId[$sku_conf] ?? 0;
            if ($id_conf === 0)
                continue;

            foreach ($skus as $sku_simple) {
                $id_simple = $skuToPostId[$sku_simple] ?? 0;
                if ($id_simple === 0)
                    continue;

                $processed++;

                // delete variation if price is 0
                if ($prezzo === 0) {
                    Log::write("Elimino la variante del prodotto $sku_simple", Log::INFO, Log::CONTEXT_PRODUCT_UPDATE);
                    $variation = new \WC_Product_Variation($id_simple);
                    $variation->delete(true);
                    continue;
                }

                // Calculate recharged price using pre-fetched meta
                $brandId = (int) ($metaMap[$id_simple]['brand_id'] ?? 0);
                $catIds = json_decode($metaMap[$id_simple]['cat_ids'] ?? '[]', true) ?: [];
                $priceRecharger = new PriceRecharger($prezzo);
                $finalPrice = $priceRecharger->getPrice($brandId, $catIds);

                if (self::DRY_RUN) {
                    $regular_price = get_post_meta($id_simple, '_regular_price', true);
                    Log::write("Aggiorno il prezzo del prodotto $sku_simple, prezzo originale: $regular_price, prezzo ricevuto: $prezzo, prezzo con ricarico: $finalPrice", Log::INFO, Log::CONTEXT_PRODUCT_UPDATE);
                    continue;
                }

                // Direct meta update instead of WC product load+save
                update_post_meta($id_simple, '_regular_price', wc_format_decimal($finalPrice));
                update_post_meta($id_simple, '_price', wc_format_decimal($finalPrice));
                update_post_meta($id_simple, 'original_base_price', $prezzo);
                wc_delete_product_transients($id_simple);
            }
        }

        Log::write("Ho aggiornato i prezzi da $date ad adesso, " . count($prices) . " record presenti e $processed record aggiornati", Log::INFO, Log::CONTEXT_CAMAC_API);

        $this->updateExecutionTime();
        return true;
    }
}
