<?php

namespace PortalPeople\zupa\services;

use Yii;
use yii\base\BaseObject;
use backend\models\Cartsbe;
use backend\models\Cartlines;
use backend\models\Paymentresponsesbe;
use backend\models\Shipment;
use PortalPeople\zupa\Module;
use PortalPeople\zupa\models\Cart;

class ProcessFilesService extends BaseObject
{
    public string $inboxPath = '';
    public string $processedPath = '';
    public string $failedPath = '';
    public string $cartClass = Cart::class;
    public ?Module $module = null;

    /** @var resource|false|null */
    private $ftpConn = null;

    public function process(): array
    {
        $result = [
            'success' => false,
            'processed' => 0,
            'created' => 0,
            'failed' => 0,
            'errors' => [],
        ];

        if (!is_dir($this->inboxPath)) {
            return $result;
        }

        $files = glob($this->inboxPath . '/*.xml');
        if (!is_array($files)) {
            $files = [];
        }

        // Open FTP connection once for all post-process moves
        if ($this->module && $this->module->moveAfterDownload) {
            $this->openFtpConnection();
        }

        try {
            foreach ($files as $filePath) {
                $filename = basename($filePath);

                try {
                    $content = file_get_contents($filePath);
                    if ($content === false) {
                        throw new \RuntimeException("Cannot read file: {$filename}");
                    }

                    $orders = $this->parseXml($content);
                    $imported = 0;

                    foreach ($orders as $orderData) {
                        if ($this->importOrder($orderData)) {
                            $imported++;
                        }
                    }

                    $result['created'] += $imported;
                    $result['processed']++;

                    $this->moveToProcessed($filePath);
                    $this->ftpMoveToArchive($filename);
                } catch (\Throwable $e) {
                    Yii::error(
                        "Failed to process {$filename}: " . $e->getMessage(),
                        'zupa.process'
                    );
                    $result['errors'][] = [
                        'file' => $filename,
                        'error' => $e->getMessage(),
                    ];
                    $result['failed']++;

                    $this->moveToFailed($filePath);
                    $this->ftpMoveToRoot($filename);
                }
            }
        } finally {
            $this->closeFtpConnection();
        }

        $result['success'] = $result['failed'] === 0;
        $this->sendSummaryEmail($result);

        return $result;
    }

    /**
     * @throws \RuntimeException
     */
    private function parseXml(string $content): array
    {
        libxml_use_internal_errors(true);
        $dom = new \DOMDocument();
        $ok  = $dom->loadXML($content);
        $errs = libxml_get_errors();
        libxml_clear_errors();
        libxml_use_internal_errors(false);

        if (!$ok) {
            $msg = 'Invalid XML format (content_size=' . strlen($content) . ')';
            if (!empty($errs)) {
                $msg .= ': ' . implode('; ', array_map(fn($e) => "L{$e->line} " . trim($e->message), $errs));
            }
            throw new \RuntimeException($msg);
        }

        $xml = simplexml_import_dom($dom);

        $nsSh     = 'http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader';
        $nsEanucc = 'urn:ean.ucc:2';
        $nsOrder  = 'urn:ean.ucc:order:2';

        // Register namespaces for XPath
        $xml->registerXPathNamespace('sh',     $nsSh);
        $xml->registerXPathNamespace('eanucc', $nsEanucc);
        $xml->registerXPathNamespace('order',  $nsOrder);

        $xp = fn(string $expr) => $xml->xpath($expr)[0] ?? null;

        // ---- Header fields ----
        $orderNumber   = (string) ($xp('//sh:InstanceIdentifier') ?? '');
        $createdAt     = (string) ($xp('//sh:CreationDateAndTime') ?? '');
        $contactName   = (string) ($xp('//sh:Contact') ?? '');
        $contactEmail  = (string) ($xp('//sh:EmailAddress') ?? '');

        if (empty($orderNumber)) {
            throw new \RuntimeException('Could not extract order number from XML');
        }

        // ---- Buyer account code ----
        $accountCode = '';
        $identNodes = $xml->xpath(
            '//buyer/additionalPartyIdentification[additionalPartyIdentificationType="SELLER_ASSIGNED_IDENTIFIER_FOR_A_PARTY"]/additionalPartyIdentificationValue'
        );
        if (!empty($identNodes)) {
            $accountCode = (string) $identNodes[0];
        }
        if (empty($accountCode)) {
            $accountCode = $this->module?->defaultAccountCode ?? 'ZUPA001';
        }

        // ---- Delivery date ----
        $deliveryDate = (string) ($xp('//requestedDeliveryDate/date') ?? '');

        // ---- Order creation timestamp ----
        $dateOrdered = !empty($createdAt) ? strtotime($createdAt) : time();
        if (!$dateOrdered) {
            $dateOrdered = time();
        }

        // ---- Line items ----
        $lineItems = $xml->xpath('//orderLineItem');
        $lines     = [];
        foreach ($lineItems as $item) {
            $qty      = (float) ($item->requestedQuantity->value ?? 1);
            $uom      = (string) ($item->requestedQuantity->unitOfMeasure->measurementUnitCodeValue ?? '');
            $price    = (float) ($item->netPrice->amount->monetaryAmount ?? 0);
            $total    = (float) ($item->netAmount->amount->monetaryAmount ?? $qty * $price);

            // Find supplier code and buyer description from additionalTradeItemIdentification elements
            $supplierCode  = '';
            $description   = '';
            foreach ($item->tradeItemIdentification->additionalTradeItemIdentification ?? [] as $tid) {
                $type  = (string) $tid->additionalTradeItemIdentificationType;
                $value = (string) $tid->additionalTradeItemIdentificationValue;
                if ($type === 'SUPPLIER_ASSIGNED') {
                    $supplierCode = $value;
                } elseif ($type === 'BUYER_ASSIGNED') {
                    $description = $value;
                }
            }

            if (empty($description)) {
                $description = $supplierCode ?: 'Item';
            }

            // Include UOM in description if meaningful
            $displayDesc = $description;
            if (!empty($uom) && $uom !== 'EA') {
                $displayDesc = "{$description} ({$uom})";
            }

            $lines[] = [
                'description' => $displayDesc,
                'stock_code'  => $supplierCode,
                'qty'         => $qty,
                'price'       => $price,
                'linetotal'   => $total,
            ];
        }

        return [[
            'ordernumber'   => $orderNumber,
            'name'          => $contactName,
            'email'         => $contactEmail,
            'contact'       => $contactName,
            'account_code'  => $accountCode,
            'deliverydate'  => $deliveryDate,
            'dateordered'   => $dateOrdered,
            'deliveryaddress' => '',
            'postcode'      => '',
            'telephone'     => '',
            'status'        => 'Paid',
            'paymentmethod' => 'Zupa',
            'shippingmethod' => 'Delivery',
            'message'       => '',
            'lines'         => $lines,
        ]];
    }

    private function importOrder(array $orderData): bool
    {
        try {
            // Skip duplicate orders
            $cartClass = $this->cartClass;
            if ($cartClass::find()->where(['porderref' => $orderData['ordernumber']])->exists()) {
                Yii::info("Skipping duplicate order: {$orderData['ordernumber']}", 'zupa.import');
                return false;
            }

            $cart = new $cartClass();
            $cart->id = $cart::tempId() ?? Yii::$app->security->generateRandomString(16);
            $cart->porderref = $orderData['ordernumber'];
            $cart->name = $orderData['name'];
            $cart->email = $orderData['email'];
            $cart->contact = $orderData['contact'];
            $cart->deliveryaddress = $orderData['deliveryaddress'];
            $cart->postcode = $orderData['postcode'];
            $cart->telephone = $orderData['telephone'];
            $cart->account_code = $orderData['account_code'];
            $cart->status = $orderData['status'];
            $cart->paymentmethod = $orderData['paymentmethod'];
            $cart->shippingmethod = $orderData['shippingmethod'];
            $cart->message = $orderData['message'];
            $cart->sourceurl = 'zupa';
            $cart->dateordered = $orderData['dateordered'] ?? time();
            $cart->nooflines = count($orderData['lines']);

            if (!$cart->save()) {
                Yii::warning(
                    'Cart save failed: ' . implode('; ', array_map(
                        fn ($msgs) => implode(', ', (array) $msgs),
                        $cart->getErrors()
                    )),
                    'zupa.import'
                );

                return false;
            }

            $lineTotal = 0;
            foreach ($orderData['lines'] as $lineData) {
                $line = new Cartlines();
                $line->basketid = $cart->id;
                $line->product_description = $lineData['description'];
                $line->product_name = $lineData['description'];
                $line->stock_code = $lineData['stock_code'] ?? '';
                $line->qty = (int) round($lineData['qty']);
                $line->price = (float) $lineData['price'];
                $line->linetotal = (float) $lineData['linetotal'];
                $lineTotal += $line->linetotal;

                if (!$line->save()) {
                    Yii::warning(
                        "Cartline save failed for cart {$cart->id}: " . implode('; ', array_map(
                            fn ($msgs) => implode(', ', (array) $msgs),
                            $line->getErrors()
                        )),
                        'zupa.import'
                    );
                }
            }

            $payment = new Paymentresponsesbe();
            $payment->basketid = $cart->id;
            $payment->source = 'zupa';
            $payment->camount = $lineTotal;
            $payment->amount = $lineTotal;
            $payment->status = 'Paid';
            $payment->paymentmethod = $orderData['paymentmethod'];
            $payment->save();

            $shipment = new Shipment();
            $shipment->cart_id = $cart->id;
            $shipment->shipment_type = 0;
            $shipment->delivery = $orderData['shippingmethod'];
            $deliveryDate = !empty($orderData['deliverydate'])
                ? date('Y-m-d', strtotime($orderData['deliverydate']))
                : date('Y-m-d');
            $shipment->shipment_date = $deliveryDate;
            $shipment->save();

            Yii::info("Order imported from Zupa: {$orderData['ordernumber']} → cart {$cart->id}", 'zupa.import');

            return true;
        } catch (\Throwable $e) {
            Yii::error(
                'Order import exception: ' . $e->getMessage() . ' | Data: ' . json_encode($orderData),
                'zupa.import'
            );

            return false;
        }
    }

    private function moveToProcessed(string $filePath): void
    {
        $filename = basename($filePath);
        $dest = $this->processedPath . DIRECTORY_SEPARATOR . $filename;

        if (!is_dir($this->processedPath)) {
            @mkdir($this->processedPath, 0755, true);
        }

        @rename($filePath, $dest);
    }

    private function moveToFailed(string $filePath): void
    {
        $filename = basename($filePath);
        $dest = $this->failedPath . DIRECTORY_SEPARATOR . $filename;

        if (!is_dir($this->failedPath)) {
            @mkdir($this->failedPath, 0755, true);
        }

        @rename($filePath, $dest);
    }

    private function openFtpConnection(): void
    {
        $m = $this->module;
        if (!$m) {
            return;
        }

        try {
            $conn = @ftp_ssl_connect($m->host, $m->port, 15);
            if (!$conn) {
                Yii::warning("Zupa FTP: could not connect to {$m->host}:{$m->port}", 'zupa');
                $this->ftpConn = false;
                return;
            }
            if (!@ftp_login($conn, $m->username, $m->password)) {
                Yii::warning("Zupa FTP: login failed for {$m->username}@{$m->host}", 'zupa');
                ftp_close($conn);
                $this->ftpConn = false;
                return;
            }
            if ($m->passiveMode) {
                ftp_pasv($conn, true);
            }
            $this->ftpConn = $conn;
        } catch (\Throwable $e) {
            Yii::warning("Zupa FTP connect error: " . $e->getMessage(), 'zupa');
            $this->ftpConn = false;
        }
    }

    private function closeFtpConnection(): void
    {
        if ($this->ftpConn) {
            @ftp_close($this->ftpConn);
        }
        $this->ftpConn = null;
    }

    private function ftpMoveToArchive(string $filename): void
    {
        if (!$this->ftpConn || !$this->module) {
            return;
        }
        $from = rtrim($this->module->moveToPath, '/') . '/' . $filename;
        $to   = rtrim($this->module->remotePath, '/') . '/' . $filename;
        if (!@ftp_rename($this->ftpConn, $from, $to)) {
            Yii::warning("Zupa FTP: could not move {$from} → {$to}", 'zupa');
        } else {
            Yii::info("Zupa FTP: archived {$filename} to {$to}", 'zupa');
        }
    }

    private function ftpMoveToRoot(string $filename): void
    {
        if (!$this->ftpConn || !$this->module) {
            return;
        }
        $from = rtrim($this->module->moveToPath, '/') . '/' . $filename;
        $to   = '/' . $filename;
        if (!@ftp_rename($this->ftpConn, $from, $to)) {
            Yii::warning("Zupa FTP: could not move {$from} → {$to}", 'zupa');
        } else {
            Yii::info("Zupa FTP: returned failed file {$filename} to FTP root", 'zupa');
        }
    }

    private function sendSummaryEmail(array $result): void
    {
        if (!$this->module || !$this->module->summaryEmail) {
            return;
        }

        try {
            $body = "Zupa Import Run Summary\n";
            $body .= str_repeat('=', 40) . "\n\n";
            $body .= "Processed files: {$result['processed']}\n";
            $body .= "Orders created: {$result['created']}\n";
            $body .= "Failed: {$result['failed']}\n\n";

            if (!empty($result['errors'])) {
                $body .= "Errors:\n";
                foreach ($result['errors'] as $err) {
                    $body .= "  - {$err['file']}: {$err['error']}\n";
                }
            }

            $body .= "\nRun at: " . date('Y-m-d H:i:s') . "\n";

            Yii::$app->mailer
                ->compose()
                ->setTo($this->module->summaryEmail)
                ->setFrom(['noreply@example.com' => 'Zupa Import'])
                ->setSubject("[Zupa] Import Summary - " . date('Y-m-d H:i'))
                ->setTextBody($body)
                ->send();
        } catch (\Throwable $e) {
            Yii::error('Failed to send Zupa summary email: ' . $e->getMessage(), 'zupa');
        }
    }
}
