<?php

/**
 * Invoice Ninja (https://invoiceninja.com).
 *
 * @link https://github.com/invoiceninja/invoiceninja source repository
 *
 * @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
 *
 * @license https://www.elastic.co/licensing/elastic-license
 */

namespace App\Jobs\EDocument;

use App\Models\Account;
use App\Models\Activity;
use App\Models\Company;
use App\Models\Credit;
use App\Models\Invoice;
use App\Services\EDocument\Gateway\Storecove\EInvoiceForwarder;
use App\Services\EDocument\Gateway\Storecove\Storecove;
use App\Services\Email\Email;
use App\Services\Email\EmailObject;
use App\Utils\Ninja;
use App\Utils\TempFile;
use App\Utils\Traits\Notifications\UserNotifies;
use App\Utils\Traits\SavesDocuments;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Mail\Mailables\Address;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\App;

class EInvoicePullDocs implements ShouldQueue
{
    use Dispatchable;
    use InteractsWithQueue;
    use Queueable;
    use SerializesModels;
    use SavesDocuments;
    use UserNotifies;

    public $deleteWhenMissingModels = true;

    public $tries = 1;

    private int $einvoice_received_count = 0;

    public function __construct() {}

    public function handle()
    {
        nlog("Pulling Peppol Docs " . now()->format('Y-m-d h:i:s'));

        if (Ninja::isHosted()) {
            return;
        }

        Account::query()
                ->with('companies')
                ->where('e_invoice_quota', '>', 0)
                ->whereHas('companies', function ($q) {
                    $q->whereNotNull('legal_entity_id');
                })
                ->cursor()
                ->each(function ($account) {

                    $account->companies->filter(function ($company) {
                        return $company->settings->e_invoice_type == 'PEPPOL';
                        })
                        ->each(function ($company) {

                            $this->einvoice_received_count = 0;
                            $this->getStatuses($company);
                            $this->getSentDocs($company);

                        });
                });
    }

    private function getSentDocs(Company $company)
    {
        
        $response = \Illuminate\Support\Facades\Http::baseUrl(config('ninja.hosted_ninja_url'))
                ->withHeaders([
                    'Content-Type' => 'application/json',
                    'Accept' => 'application/json',
                    'X-EInvoice-Token' => $company->account->e_invoicing_token,
                ])
                ->post('/api/einvoice/peppol/documents', data: [
                    'license_key' => config('ninja.license_key'),
                    'account_key' => $company->account->key,
                    'company_key' => $company->company_key,
                    'legal_entity_id' => $company->legal_entity_id,
                ]);

            if ($response->successful()) {

                $hash = $response->header('X-CONFIRMATION-HASH');

                $this->handleSuccess($response->json(), $company, $hash);
            } else {
                nlog($response->body());
            }

            if ($this->einvoice_received_count > 0) {

                foreach ($company->company_users as $company_user) {

                    $user = $company_user->user;

                    $notifications = $this->findCompanyUserNotificationType($company_user, ['enable_e_invoice_received_notification']);

                    if (!in_array('mail', $notifications)) {
                        continue;
                    }

                    App::setLocale($company->getLocale());

                    $mo = new EmailObject();
                    $mo->subject = ctrans('texts.einvoice_received_subject');
                    $mo->body = ctrans('texts.einvoice_received_body', ['count' => $this->einvoice_received_count]);
                    $mo->text_body = ctrans('texts.einvoice_received_body', ['count' => $this->einvoice_received_count]);
                    $mo->company_key = $company->company_key;
                    $mo->html_template = 'email.template.admin';
                    $mo->to = [new Address($user->email, $user->present()->name())];

                    Email::dispatch($mo, $company);
                }
            }

            $this->pullSentDocuments($company);
    }

    private function getStatuses(Company $company)
    {
        
        $response = \Illuminate\Support\Facades\Http::baseUrl(config('ninja.hosted_ninja_url'))
                ->withHeaders([
                    'Content-Type' => 'application/json',
                    'Accept' => 'application/json',
                    'X-EInvoice-Token' => $company->account->e_invoicing_token,
                ])
                ->post('/api/einvoice/peppol/documents/statuses', data: [
                    'license_key' => config('ninja.license_key'),
                    'account_key' => $company->account->key,
                    'company_key' => $company->company_key,
                    'legal_entity_id' => $company->legal_entity_id,
                ]);

                if ($response->successful()) {
                    $statuses = $response->json();
                
                    foreach ($statuses as $status) {
                
                        $model = Invoice::withTrashed()->where('backup->guid', $status['guid'])->first();

                        if(!$model){
                            $model = Credit::withTrashed()->where('backup->guid', $status['guid'])->first();
                        }

                        if(!$model){
                            /**
                             * France e-reporting submission GUIDs live on
                             * TransactionEvent.payment_request->guid, not on an invoice/credit
                             * backup->guid. Hand the status to the FR reconciler, which matches
                             * by that key. On self-hosted multi_db is disabled, so the reconciler
                             * resolves the local company and TransactionEvent directly.
                             */
                            UpdateFranceEReportSubmissionStatus::dispatch([
                                'tenant_id' => $company->company_key,
                                'guid' => $status['guid'],
                                'event' => $status['event'] ?? null,
                                'event_group' => $status['event_group'] ?? null,
                            ]);

                            continue;
                        }

                        $statusEvent = (string) ($status['event'] ?? '');
                        $this->recordDocumentStatus($model, $statusEvent);

                        match($statusEvent){
                            'cleared' => $this->writeActivity($model, Activity::EINVOICE_STATUS_UPDATED, ctrans('texts.peppol_cleared_for_sending')),
                            'accepted' => $this->writeActivity($model, Activity::EINVOICE_STATUS_UPDATED, ctrans('texts.peppol_accepted')),
                            'rejected' => $this->writeActivity($model, Activity::EINVOICE_STATUS_UPDATED, ctrans('texts.peppol_rejected')),
                            'partially_paid' => $this->writeActivity($model, Activity::EINVOICE_STATUS_UPDATED, ctrans('texts.peppol_partially_paid')),
                            'paid' => $this->writeActivity($model, Activity::EINVOICE_STATUS_UPDATED, ctrans('texts.peppol_paid')),                            
                            default => null,
                        };

                    }
                
                }

    }


    private function recordDocumentStatus(Invoice|Credit $model, string $status): void
    {
        if ($status === '') {
            return;
        }

        $model->backup->e_invoice_status = $status;

        if ($status === 'cleared' && is_null($model->backup->e_invoice_cleared_at)) {
            $model->backup->e_invoice_cleared_at = now()->toIso8601String();
        }

        $model->saveQuietly();
    }

    private function writeActivity($model, int $activity_id, ?string $notes = '')
    {
        $model_key = 'invoice_id';
        
        if($model instanceof Credit) {
            $model_key = 'credit_id';
        }

        if(Activity::where($model_key, $model->id)
        ->where('activity_type_id', $activity_id)
        ->where('notes', $notes)
        ->where('created_at', '>', now()->subDays(1))
        ->exists()){
            return;
        }

        $activity = new Activity();
        $activity->user_id = $model->user_id;
        $activity->client_id = $model->client_id ?? $model->vendor_id;
        $activity->company_id = $model->company_id;
        $activity->account_id = $model->company->account_id;
        $activity->activity_type_id = $activity_id;
        $activity->{$model_key} = $model->id;
        $activity->notes = $notes;
        $activity->save();
    }

    /**
     * Processes received documents pulled from the hosted server.
     *
     * Creates expenses and vendors from each Storecove invoice, saves
     * HTML/XML/attachments to the expense, and forwards the XML to
     * the company's expense forwarding email if configured. Flushes the
     * documents from S3 after processing.
     *
     * @param  array<int, array>  $received_documents
     * @param  Company            $company
     * @param  string             $hash  Confirmation hash for flushing
     * @return void
     */
    private function handleSuccess(array $received_documents, Company $company, string $hash): void
    {

        $storecove = new Storecove();
        $forwarder = EInvoiceForwarder::forExpenses($company);

        foreach ($received_documents as $document) {

            nlog($document);

            if (!isset($document['document']['invoice'])) {
                nlog("No invoice found in document!!");
                continue;
            }

            $storecove_invoice = $storecove->expense->getStorecoveInvoice(json_encode($document['document']['invoice']));
            $expense = $storecove->expense->createExpense($storecove_invoice, $company);

            $file_name = $document['guid'];

            if (strlen($document['html'] ?? '') > 5) {

                $upload_document = TempFile::UploadedFileFromRaw($document['html'], "{$file_name}.html", 'text/html');
                $this->saveDocument($upload_document, $expense, true);
                $upload_document = null;
            }

            if (strlen($document['original_base64_xml'] ?? '') > 5) {

                $upload_document = TempFile::UploadedFileFromBase64($document['original_base64_xml'], "{$file_name}.xml", 'application/xml');
                $this->saveDocument($upload_document, $expense, true);
                $upload_document = null;

                if ($forwarder->isConfigured()) {
                    $forwarder->forward(base64_decode($document['original_base64_xml']), "{$file_name}.xml", 'received');
                }
            }

            if (isset($document['document']['invoice']['attachments'])) {
                foreach ($document['document']['invoice']['attachments'] as $attachment) {

                    $upload_document = TempFile::UploadedFileFromBase64($attachment['document'], $attachment['filename'], $attachment['mime_type']);
                    $this->saveDocument($upload_document, $expense, true);
                    $upload_document = null;

                }
            }

            $this->einvoice_received_count++;

        }

        $response = \Illuminate\Support\Facades\Http::baseUrl(config('ninja.hosted_ninja_url'))
            ->withHeaders([
                'Content-Type' => 'application/json',
                'Accept' => 'application/json',
                'X-EInvoice-Token' => $company->account->e_invoicing_token,
            ])
            ->post('/api/einvoice/peppol/documents/flush', data: [
                'license_key' => config('ninja.license_key'),
                'account_key' => $company->account->key,
                'company_key' => $company->company_key,
                'legal_entity_id' => $company->legal_entity_id,
                'hash'  => $hash,
            ]);

        if ($response->successful()) {
        }



    }

    /**
     * Pulls confirmed-sent XML documents from the hosted server and forwards
     * them to the company's forwarding email.
     *
     * Only runs when the company has a valid forwarding email configured.
     * Retrieves sent documents via /api/einvoice/peppol/documents/sent,
     * forwards each XML, then flushes the documents from S3.
     *
     * @param  Company $company
     * @return void
     */
    private function pullSentDocuments(Company $company): void
    {
        $forwarder = new EInvoiceForwarder($company);

        if (!$forwarder->isConfigured()) {
            return;
        }

        $response = \Illuminate\Support\Facades\Http::baseUrl(config('ninja.hosted_ninja_url'))
            ->withHeaders([
                'Content-Type' => 'application/json',
                'Accept' => 'application/json',
                'X-EInvoice-Token' => $company->account->e_invoicing_token,
            ])
            ->post('/api/einvoice/peppol/documents/sent', data: [
                'license_key' => config('ninja.license_key'),
                'account_key' => $company->account->key,
                'company_key' => $company->company_key,
                'legal_entity_id' => $company->legal_entity_id,
            ]);

        if (!$response->successful()) {
            return;
        }

        $sent_documents = $response->json();
        $hash = $response->header('X-CONFIRMATION-HASH');

        if (empty($sent_documents)) {
            return;
        }

        foreach ($sent_documents as $document) {
            $guid = $document['guid'] ?? '';
            $xml_base64 = $document['xml_base64'] ?? '';

            if (strlen($xml_base64) > 5) {
                $forwarder->forward(base64_decode($xml_base64), "{$guid}.xml", 'sent');
            }
        }

        \Illuminate\Support\Facades\Http::baseUrl(config('ninja.hosted_ninja_url'))
            ->withHeaders([
                'Content-Type' => 'application/json',
                'Accept' => 'application/json',
                'X-EInvoice-Token' => $company->account->e_invoicing_token,
            ])
            ->post('/api/einvoice/peppol/documents/sent/flush', data: [
                'license_key' => config('ninja.license_key'),
                'account_key' => $company->account->key,
                'company_key' => $company->company_key,
                'legal_entity_id' => $company->legal_entity_id,
                'hash' => $hash,
            ]);
    }

    public function failed(\Throwable $exception)
    {
        nlog($exception->getMessage());
    }
}
