<?php

namespace App\Classes\Modules\Transactions\Processors;


use App\Classes\Exceptions\ResourceNotFoundException;
use App\Classes\Modules\Anthropic\Services\CreatesClaudeResponse;
use App\Classes\Modules\Bookings\Services\FetchesBooking;
use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber;
use App\Classes\Modules\Transactions\Processors\CreatePurchaseOrderTransactionProcessor;
use App\Classes\Modules\Transactions\DataTransferObjects\TransactionObject;
use App\Classes\Modules\OpenAI\Services\CreatesChatGPTResponse;
use App\Classes\ValueObjects\Constants\KVPKey;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\PaymentMethodType;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Models\KeyValuePair;
use App\Models\Booking;
use Illuminate\Support\Facades\Log;

//This solution has partial dependency on pdf table extractor on front end (output not ideal)
class ETLPurchaseOrderTransactionProcessor
{
    /** @var CreatesChatGPTResponse */
    private $createsChatGPTResponse;

    /** @var FetchesBooking */
    private $fetchesBooking;

    /** @var GeneratesTransactionBillNumber */
    private $generatesTransactionBillNumber;

    /** @var CreatePurchaseOrderTransactionProcessor */
    private $createPurchaseOrderTransactionProcessor;

    /** @var CreatesClaudeResponse */
    private $createsClaudeResponse;

    /**
     * ETLPurchaseOrderTransactionProcessor constructor.
     * @param FetchesBooking $fetchesBooking
     * @param GeneratesTransactionBillNumber $generatesTransactionBillNumber
     * @param CreatePurchaseOrderTransactionProcessor $createPurchaseOrderTransactionProcessor
     * @param CreatesChatGPTResponse $createsChatGPTResponse
     * @param CreatesClaudeResponse $createsClaudeResponse
     */
    public function __construct(FetchesBooking $fetchesBooking, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CreatePurchaseOrderTransactionProcessor $createPurchaseOrderTransactionProcessor, CreatesChatGPTResponse $createsChatGPTResponse, CreatesClaudeResponse $createsClaudeResponse)
    {
        $this->fetchesBooking = $fetchesBooking;
        $this->generatesTransactionBillNumber = $generatesTransactionBillNumber;
        $this->createPurchaseOrderTransactionProcessor = $createPurchaseOrderTransactionProcessor;
        $this->createsChatGPTResponse = $createsChatGPTResponse;
        $this->createsClaudeResponse = $createsClaudeResponse;
    }

    /**
     * @param array $products
     * @param array $productsMetadata
     * @param int|null $bookingId
     * @return array
     * @throws \App\Classes\Exceptions\MalformedRequestException
     * @throws \App\Classes\Exceptions\ResourceNotFoundException
     * @throws \Exception
     */
    public function execute(array $products, array $productsMetadata, ?int $bookingId = null)
    {
        foreach ($products as &$product) {
            $product['quantity'] = (int) $product['quantity'];
            $product['unit_price'] = (float) $product['unit_price'];
        }

        // Translate descriptions if they contain Chinese characters
        $products = collect($products)->map(function ($product) {
            if (isset($product['description']) && preg_match("/\p{Han}+/u", $product['description'])) {
                $kvp = KeyValuePair::where('key', KVPKey::CLAUDE_PROMPT_PREFIX . "TRANSLATE_1")->first();
                $userPrompt = '';
                if($kvp){
                    $templateFromDb = $kvp->value;
                    $productsMetadataJson = json_encode($product['description'], JSON_UNESCAPED_UNICODE);
                    $userPrompt = str_replace('{{productDescription}}', $productsMetadataJson, $templateFromDb);
                }
                else{
                    throw new ResourceNotFoundException('AI Setup incomplete!');
                }

                // $result = $this->createsChatGPTResponse->execute($userPrompt);
                $result = $this->createsClaudeResponse->execute($userPrompt);
                $content = $result['choices'][0]['message']['content'] ?? null;

                // 1. Try raw JSON
                $decoded = json_decode($content, true);
                if (json_last_error() === JSON_ERROR_NONE) {
                    $json = $content;
                }
                // 2. Fallback to fenced ```json block
                elseif (preg_match('/```json\s*(\[[\s\S]*?\]|\{[\s\S]*?\})\s*```/i', $content, $matches)) {
                    $json = $matches[1];
                }
                else {
                    Log::warning('No JSON found in AI service response', ['content' => $content]);
                    return;
                }

                $translations = json_decode($json, true);
                if($translations){
                    $translatedText = $translations[0];
                    $product['description'] = $translatedText;
                }
            }
            return $product;
        })->toArray();

       if (is_array($productsMetadata) && count($productsMetadata) > 0) {
            $kvp = KeyValuePair::where('key', KVPKey::CLAUDE_PROMPT_PREFIX . "EXTRACT_1")->first();
            $prompt = '';
            if($kvp){
                $templateFromDb = $kvp->value;
                $productsMetadataJson = json_encode($productsMetadata, JSON_UNESCAPED_UNICODE);
                $prompt = str_replace('{{productsMetadata}}', $productsMetadataJson, $templateFromDb);
            }
            else{
                throw new ResourceNotFoundException('AI Setup incomplete!');
            }

            // $result = $this->createsChatGPTResponse->execute($prompt);
            $result = $this->createsClaudeResponse->execute($prompt);
            $content = $result['choices'][0]['message']['content'] ?? null;

            $freight = 0;
            $preferential = 0;

            if ($content) {
                if (preg_match('/```json\s*(\{.*?\})\s*```/s', $content, $matches)) { //If wrapped in ```json, extract it
                    $json = $matches[1];
                } else {
                    $json = trim($content);
                }

                $fees = json_decode($json);

                if (json_last_error() === JSON_ERROR_NONE) {
                    $freight = $fees->Freight ?? 0;
                    $preferential = $fees->Preferential ?? 0;
                } else {
                    Log::error('JSON decode failed', [
                        'error' => json_last_error_msg(),
                        'content' => $content
                    ]);
                }
            }

            // Add Freight as a separate product
            if($freight){
                $products[] = [
                    'stockCode' => '',
                    'description' => 'First-Mile Delivery',
                    'quantity'    => 1,
                    'unit_price'  => $freight,

                ];
            }

            // Add Preferential as a separate product (as negative price if needed)
            if($preferential){
                $products[] = [
                    'stockCode' => '',
                    'description' => 'Discount',
                    'quantity'    => 1,
                    'unit_price'  => $preferential,
                ];
            }
        }

        if($bookingId){
            /** @var Booking $booking */
            $booking = $this->fetchesBooking->execute(['id' => $bookingId]);

            $billNumber = $this->generatesTransactionBillNumber->execute('PO-');

            $total = collect($products)->sum(function($product){
                return $product['quantity'] * floatval(str_replace(',', '', $product['unit_price']));
            });

            $object = new TransactionObject($billNumber, TransactionType::PURCHASE_ORDER, $booking->company->id, 1,
                1, PaymentMethodType::CASH,
                $total, $total, $booking->fix_currency_id, $booking->fix_currency_id,
                1, 0, 0, null, ApprovalStatus::PENDING_SUBMISSION, $products);


            $transaction = $this->createPurchaseOrderTransactionProcessor->execute($booking, $object);
        }

        return $products;
    }
}
