<?php

namespace App\Classes\Modules\Transactions\Processors;


use App\Classes\Modules\OpenAI\Services\UploadsOpenAIFiles;
use App\Classes\Modules\OpenAI\Services\CreatesChatGPTResponseWithFiles;
use App\Classes\Exceptions\ResourceNotFoundException;
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\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;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;

class ETLPDFPurchaseOrderChatGPTProcessor
{
    /** @var FetchesBooking */
    private $fetchesBooking;

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

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

    /** @var UploadsOpenAIFiles */
    private $uploadsOpenAIFiles;

    /** @var CreatesChatGPTResponseWithFiles */
    private $createsChatGPTResponseWithFiles;

    /**
     * ETLPDFPurchaseOrderChatGPTProcessor constructor.
     * @param FetchesBooking $fetchesBooking
     * @param GeneratesTransactionBillNumber $generatesTransactionBillNumber
     * @param CreatePurchaseOrderTransactionProcessor $createPurchaseOrderTransactionProcessor
     * @param UploadsOpenAIFiles $uploadsOpenAIFiles
     * @param CreatesChatGPTResponseWithFiles $createsChatGPTResponseWithFiles
     */
    public function __construct(FetchesBooking $fetchesBooking, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CreatePurchaseOrderTransactionProcessor $createPurchaseOrderTransactionProcessor, UploadsOpenAIFiles $uploadsOpenAIFiles,
    CreatesChatGPTResponseWithFiles $createsChatGPTResponseWithFiles)
    {
        $this->fetchesBooking = $fetchesBooking;
        $this->generatesTransactionBillNumber = $generatesTransactionBillNumber;
        $this->createPurchaseOrderTransactionProcessor = $createPurchaseOrderTransactionProcessor;
        $this->uploadsOpenAIFiles = $uploadsOpenAIFiles;
        $this->createsChatGPTResponseWithFiles = $createsChatGPTResponseWithFiles;
    }


    /**
     * @param UploadedFile[] $files
     * @param int $bookingId
     * @return array|null
     */
    public function execute(array $files, ?int $bookingId = null): ?array
    {
        $tempPaths = [];

        try {
            foreach ($files as $index => $fileBase64) {
                // Strip prefix if present
                if (preg_match('/^data:application\/pdf;base64,/', $fileBase64)) {
                    $fileBase64 = preg_replace('/^data:application\/pdf;base64,/', '', $fileBase64);
                }

                // Decode Base64
                $fileContent = base64_decode($fileBase64);
                if ($fileContent === false) {
                    Log::warning("Invalid Base64 for file index $index");
                    continue;
                }

                // Ensure temp directory exists
                $tempDir = storage_path('app/documents');
                if (!file_exists($tempDir)) {
                    mkdir($tempDir, 0755, true);
                }

                // Generate unique temp file path
                $fileName = uniqid("file_{$index}_") . '.pdf';
                $filePath = $tempDir . '/' . $fileName;

                // Save file to disk
                file_put_contents($filePath, $fileContent);

                // Track temp path
                $tempPaths[] = $filePath;
            }

            Log::info('tempPaths: ' . json_encode($tempPaths));

            // Upload to OpenAI
            $fileIds = $this->uploadsOpenAIFiles->execute($tempPaths);

            $kvp = KeyValuePair::where(
                'key',
                KVPKey::CHATGPT_PROMPT_PREFIX . 'EXTRACT_1'
            )->first();

            if (! $kvp) {
                throw new ResourceNotFoundException('AI Setup incomplete!');
            }

            $userPrompt = $kvp->value;
            $response = $this->createsChatGPTResponseWithFiles->execute($userPrompt, $fileIds);

            // Extract the text content from the first output message
            $textContent = $response['output'][0]['content'][0]['text'] ?? '[]';

            // Remove ```json fences if present
            if (preg_match('/```json\s*(.*?)\s*```/s', $textContent, $matches)) {
                $jsonString = $matches[1];
            } else {
                $jsonString = $textContent;
            }

            // Decode JSON
            $data = json_decode($jsonString, true);

            if (json_last_error() !== JSON_ERROR_NONE) {
                Log::error('Failed to decode OpenAI JSON response', [
                    'error' => json_last_error_msg(),
                    'textContent' => $textContent
                ]);
                $data = [];
            }

            // Optional: convert keys to snake_case
            $data = array_map(function($item) {
                $newItem = [];
                foreach ($item as $key => $value) {
                    $newKey = strtolower(str_replace(' ', '_', $key));
                    $newItem[$newKey] = $value;
                }
                return $newItem;
            }, $data);

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

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

                $total = collect($data)->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, $data);


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

        } catch (\Exception $e) {
            Log::error('Error processing PDF files with ChatGPT', [
                'message' => $e->getMessage(),
                'trace' => $e->getTraceAsString(),
            ]);
            throw $e;
        } finally {
            // Clean up
            foreach ($tempPaths as $path) {
                if (file_exists($path)) {
                    unlink($path);
                }
            }
        }
    }

    function convertKeysToSnakeCase(array $items): array {
        return array_map(function($item) {
            $newItem = [];
            foreach ($item as $key => $value) {
                $newKey = strtolower(str_replace(' ', '_', $key));
                $newItem[$newKey] = $value;
            }
            return $newItem;
        }, $items);
    }
}
