<?php

namespace App\Classes\Modules\Transactions\Processors;

use App\Classes\Modules\Documents\Services\CreatesDocument;
use App\Classes\Modules\Documents\Services\CreatesFiles;
use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\DocumentType;
use App\Classes\ValueObjects\Constants\KVPKey;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Models\Booking;
use App\Models\Document;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Mccarlosen\LaravelMpdf\Facades\LaravelMpdf;
use Webklex\PDFMerger\Facades\PDFMergerFacade as PDFMerger;
use Illuminate\Support\Facades\Storage;

class CreateInvoiceDocumentProcessor
{
    /** @var CreatesDocument */
    private $createsDocument;

    /** @var CreatesFiles */
    private $createsFile;

    /**
     * CreateInvoiceDocumentProcessor constructor.
     * @param CreatesDocument $createsDocument
     * @param CreatesFiles $createsFile
     */
    public function __construct(CreatesDocument $createsDocument, CreatesFiles $createsFile)
    {
        $this->createsDocument = $createsDocument;
        $this->createsFile = $createsFile;
    }

    /**
     * @param $transaction
     * @param $purchaseOrder
     * @param $supplier
     * @param $document_type
     * @return void
     * @throws \App\Classes\Exceptions\MalformedRequestException
     */
    public function execute($transaction, $purchaseOrder, $supplier, $document_type, $voucherRedemption = null, $generateEInvoiceWithNormalInvoiceTemplate = false, $generateEInvoiceRefund = false)
    {
        // calculate current Paid Amount
        $booking = $transaction->owner_type == Booking::class ? $transaction->owner : null;
        if(!$booking){
            $booking = $transaction->owner->owner_type == Booking::class ? $transaction->owner->owner : null;
        }
        $currentPaidAmount = null;
        $brn = $supplier->documents()->where('document_type', DocumentType::SSM_REGISTRATION)->latest()->first();
        $documentDate = $supplier->segments->whereIn('id', [23])->first() ? \Carbon\Carbon::now() : $booking->created_at;
        $eInvoiceStartDate = Carbon::parse(env('E_INVOICE_START_DATE', '2025-07-01 00:00:00'));
        $autoCountInvoiceId = '';
        $autoCountEInvoiceValidationLink = 'CIEF';
        $eInvoiceStartedForBooking = false;

        if ($booking) {
            $bookingCreatedDate = Carbon::parse($booking->created_at);
            if ($bookingCreatedDate->isAfter($eInvoiceStartDate)) {
                if($generateEInvoiceRefund){
                    $lastPaymentTransaction = $booking->transactions()->where('type', TransactionType::PAYMENT)->whereIn('status', [ApprovalStatus::REFUNDED])->latest()->first();
                }
                else{
                    $lastPaymentTransaction = $booking->transactions()->where('type', TransactionType::PAYMENT)->whereIn('status', [ApprovalStatus::COMPLETED, ApprovalStatus::APPROVED])->latest()->first();

                    if(!$lastPaymentTransaction){
                        $lastPaymentTransaction = $booking->transactions()->where('type', TransactionType::PAYMENT)->whereIn('status', [ApprovalStatus::REFUNDED])->latest()->first();
                    }
                }

                $documentDate = $lastPaymentTransaction->created_at;
                $eInvoiceStartedForBooking = true;
            }

            if($document_type === DocumentType::EINVOICE){
                $metadata = $booking->attributesKVP()->where('key', KVPKey::AUTOCOUNT_DOCNO_INVOICE)->first();
                if($metadata){
                    $autoCountInvoiceId = $metadata->value;
                    $metadata = $booking->attributesKVP()->where('key', KVPKey::AUTOCOUNT_EINVOICE_VALIDATION_LINK)->first();
                    if($metadata){
                        $autoCountEInvoiceValidationLink = $metadata->value;
                    }
                    $metadata = $booking->attributesKVP()->where('key', KVPKey::AUTOCOUNT_DOCDATE_INVOICE)->first();
                    if($metadata){
                        $documentDate = Carbon::parse($metadata->value);
                    }
                    else{
                        $lastDayOfMonth = $documentDate->copy()->endOfMonth();
                        $documentDate = $lastDayOfMonth;
                    }
                }
                else{
                    $metadata = $transaction->attributesKVP()->where('key', KVPKey::AUTOCOUNT_DOCNO_INVOICE)->first();
                    if($metadata){
                        $autoCountInvoiceId = $metadata->value;
                    }
                    $metadata = $transaction->attributesKVP()->where('key', KVPKey::AUTOCOUNT_EINVOICE_VALIDATION_LINK)->first();
                    if($metadata){
                        $autoCountEInvoiceValidationLink = $metadata->value;
                    }
                    $metadata = $transaction->attributesKVP()->where('key', KVPKey::AUTOCOUNT_DOCDATE_INVOICE)->first();
                    if($metadata){
                        $documentDate = Carbon::parse($metadata->value);
                    }
                    else{
                        $lastDayOfMonth = $documentDate->copy()->endOfMonth();
                        $documentDate = $lastDayOfMonth;
                    }
                }
            }

            if($generateEInvoiceRefund){
                $payment = $booking->transactions()->where('type', TransactionType::PAYMENT)->whereIn('status', [ApprovalStatus::REFUNDED])->first();
            }
            else{
                $payment = $booking->transactions()->where('type', TransactionType::PAYMENT)->whereIn('status', [ApprovalStatus::COMPLETED, ApprovalStatus::APPROVED])->first();
                if(!$payment){
                    $payment = $booking->transactions()->where('type', TransactionType::PAYMENT)->whereIn('status', [ApprovalStatus::REFUNDED])->first();
                }
            }

            $refundAmount = $payment->transactions()->refunds()->whereIn('status', [ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::APPROVED])->sum('amount');
            $paymentAmount = $payment->amount;
            $currentPaidAmount = $paymentAmount - $refundAmount;
        }

        $lowercaseDocumentType = strtolower($document_type);

        if($document_type === DocumentType::EINVOICE){ //July 2025 workaround generate normal invoice instead of E-Invoice
            if($generateEInvoiceWithNormalInvoiceTemplate){
                $lowercaseDocumentType = strtolower(DocumentType::INVOICE);
            }
        }

        $order_pdf = LaravelMpdf::loadView('pages.pdfs.' . $lowercaseDocumentType,
        [
            'transaction' => $transaction,
            'po_order_transaction' => $purchaseOrder,
            'supplier' => $supplier,
            'voucher_redemption' => $voucherRedemption,
            'current_paid_amount' => $currentPaidAmount,
            'document_date' => $documentDate,
            'brn' => $brn,
            'booking' => $booking,
            'autocountId' => $autoCountInvoiceId,
            'autocountEInvoiceValidationLink' => $autoCountEInvoiceValidationLink,
            'e_invoice_started_for_booking' => $eInvoiceStartedForBooking,
        ]);

        if($purchaseOrder && $purchaseOrder->booking->service_id === 4) {
            $purchaseOrderDocuments = $purchaseOrder->booking->documents()->where('document_type', DocumentType::ECOMMERCE_PURCHASE_ORDER)->get();


            $oMerger = PDFMerger::init();

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

            $order_pdf->save(storage_path('app/documents/temp.pdf'));

            $oMerger->addPDF(storage_path('app/documents/temp.pdf'), 'all');
            $filesystemDriver = Storage::getDefaultDriver();
            foreach ($purchaseOrderDocuments as $document){
                $currentFile = $document->files()->first()->file->file_info->original->file;
                if($filesystemDriver === 's3'){
                    if (!Storage::disk('s3')->exists($currentFile)) {
                        Log::error('S3 file missing: ' . $currentFile);
                        continue;
                    }

                    Log::info('CreateInvoiceDocumentProcessor 1: ' . $currentFile);
                    $fileContent = Storage::disk('s3')->get($currentFile);
                    $filepath = storage_path('app/documents/' . $currentFile);
                    $directoryPath = dirname($filepath);
                    if (!file_exists($directoryPath)) {
                        mkdir($directoryPath, 0755, true);
                    }
                    file_put_contents($filepath, $fileContent);
                }

                $localFilePath = storage_path('app/documents/' . $currentFile);
                $oMerger->addPDF($localFilePath, 'all');
            }

            $oMerger->merge();

            $order_pdf = $oMerger;

            if($filesystemDriver === 's3'){
                $finalTempPdfPath = storage_path('app/documents/temp.pdf');
                $filePathForS3 = 'temp/temp.pdf';
                $fileContent = file_get_contents($finalTempPdfPath);
                Log::info('CreateInvoiceDocumentProcessor 2 filePathForS3: ' . $filePathForS3);
                Storage::disk('s3')->put($filePathForS3, $fileContent);
            }
        }

        $document_object = new DocumentObject(
            $document_type,
            [chunk_split('data:application/pdf;base64,' . base64_encode($order_pdf->output()))],
            '',
            ApprovalStatus::COMPLETED,
            $lowercaseDocumentType . 's'
        );

        /** @var Document $document */
        if($document_type === DocumentType::RECEIPT_VOUCHER){
            $document = $this->createsDocument->execute($transaction, $document_object);
        }
        else{
            $document = $this->createsDocument->execute($purchaseOrder->booking ?? $booking, $document_object);
        }
        $this->createsFile->execute($document, $document_object);
    }
}
