<?php

namespace App\Classes\Modules\Transactions\Processors;


use App\Classes\Modules\Anthropic\Services\UploadsClaudeFiles;
use App\Classes\Modules\Anthropic\Services\CreatesClaudeResponseWithFiles;
use App\Classes\Exceptions\ResourceNotFoundException;
use App\Classes\Jobs\Commands\V2\ETLPurchaseOrderTransactionCompleteV2CommandJob;
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\Events\ETLPurchaseOrderTransactionCompleteEvent;
use App\Models\KeyValuePair;
use App\Models\Booking;
use Illuminate\Support\Facades\Log;
use Illuminate\Http\UploadedFile;

class ETLPDFPurchaseOrderClaudeProcessor
{
    private FetchesBooking $fetchesBooking;
    private GeneratesTransactionBillNumber $generatesTransactionBillNumber;
    private CreatePurchaseOrderTransactionProcessor $createPurchaseOrderTransactionProcessor;
    private UploadsClaudeFiles $uploadsClaudeFiles;
    private CreatesClaudeResponseWithFiles $createsClaudeResponseWithFiles;

    public function __construct(
        FetchesBooking $fetchesBooking,
        GeneratesTransactionBillNumber $generatesTransactionBillNumber,
        CreatePurchaseOrderTransactionProcessor $createPurchaseOrderTransactionProcessor,
        UploadsClaudeFiles $uploadsClaudeFiles,
        CreatesClaudeResponseWithFiles $createsClaudeResponseWithFiles
    ) {
        $this->fetchesBooking = $fetchesBooking;
        $this->generatesTransactionBillNumber = $generatesTransactionBillNumber;
        $this->createPurchaseOrderTransactionProcessor = $createPurchaseOrderTransactionProcessor;
        $this->uploadsClaudeFiles = $uploadsClaudeFiles;
        $this->createsClaudeResponseWithFiles = $createsClaudeResponseWithFiles;
    }

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

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

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

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

                $filePath = $tempDir . '/' . uniqid("file_{$index}_") . '.pdf';
                file_put_contents($filePath, $fileContent);

                $tempPaths[] = $filePath;
            }

            Log::info('Claude tempPaths', $tempPaths);

            // Upload PDFs to Claude
            $fileIds = $this->uploadsClaudeFiles->execute($tempPaths);

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

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

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

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

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

            $textContent = $response['content'][0]['text'] ?? '[]';

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

            $data = json_decode($jsonString, true);

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

            // Normalize keys to snake_case
            $data = $this->convertKeysToSnakeCase($data);
            Log::info('data: '. json_encode($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']));
                });

                Log::info('total: '. (string) $total);

                $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
                );

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

            return $data;

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

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