<?php
/**
 * Consignment Controller – GForce Module
 *
 * @category    PortalPeople
 * @package     GForce
 * @author      Portal People Development Team
 * @copyright   Copyright (c) 2026 Portal People (https://portalpeople.co.uk)
 */

namespace PortalPeople\gforce\controllers;

use Yii;
use yii\web\Controller;
use yii\web\Response;
use yii\filters\VerbFilter;
use yii\filters\AccessControl;
use backend\models\Cartsbe;
use PortalPeople\gforce\components\ConsignmentService;
use PortalPeople\gforce\models\ConsignmentForm;
use PortalPeople\gforce\jobs\ConsignmentJob;

/**
 * ConsignmentController
 *
 * Handles consignment creation, cancellation, manifesting, tracking and
 * label retrieval via the GForce API.
 */
class ConsignmentController extends Controller
{
    /** @inheritdoc */
    public function behaviors(): array
    {
        return [
            'access' => [
                'class' => AccessControl::class,
                'rules' => [
                    [
                        'allow' => true,
                        'roles' => ['@'],
                    ],
                ],
            ],
            'verbs' => [
                'class'   => VerbFilter::class,
                'actions' => [
                    'create'   => ['GET', 'POST'],
                    'delete'   => ['POST'],
                    'manifest' => ['POST'],
                    'tracking' => ['GET'],
                    'labels' => ['GET'],
                    'eta' => ['GET'],
                    'images' => ['GET'],
                    'image-data' => ['GET'],
                    'services' => ['GET'],
                ],
            ],
        ];
    }

    /**
     * Form to create a new consignment.
     *
     * Accepts an optional `id` GET parameter to pre-populate delivery address,
     * weight and reference from an existing cart.
     *
     * On POST: validates, then queues (if submitAction=queue and queue is
     * available) or sends directly to GForce. On success, tracking data
     * is written back to the cart when an id was provided.
     *
     * @return string|Response
     */
    public function actionCreate()
    {
        $model = new ConsignmentForm();
        $id    = Yii::$app->request->get('id', null);
        $cart  = $id !== null ? Cartsbe::findOne(['id' => $id]) : null;
        /** @var ConsignmentService $consignmentService */
        $consignmentService = Yii::createObject(ConsignmentService::class);

        /** @var \PortalPeople\gforce\Module $module */
        $module = Yii::$app->getModule('gforce');

        if ($cart !== null && Yii::$app->request->isGet) {
            $consignmentService->prepopulateModelFromCart($model, $module, $cart);
        }


        if ($model->load(Yii::$app->request->post()) && $model->validate()) {
            $confirm      = $module ? $module->autoConfirm : false;
            $uniqueRef    = $consignmentService->buildUniqueRef();
            $submitAction = Yii::$app->request->post('submitAction');
            $manifest     = $model->getManifest($confirm);

            try {
                $preflight = $consignmentService->validateRouteAndPreflight($model, $module, $manifest);
                if (!$preflight['ok']) {
                    Yii::$app->session->setFlash('error', $preflight['message']);
                    return $this->render('create', [
                        'model'          => $model,
                        'queueAvailable' => Yii::$app->has('queue'),
                    ]);
                }
            } catch (\Exception $e) {
                Yii::$app->session->setFlash('error', 'GForce validation check failed: ' . $e->getMessage());
                return $this->render('create', [
                    'model'          => $model,
                    'queueAvailable' => Yii::$app->has('queue'),
                ]);
            }

            // Queue only when explicitly requested and a queue component exists
            if (Yii::$app->has('queue') && $submitAction === 'queue') {
                Yii::$app->queue->push(new ConsignmentJob([
                    'manifest'  => $manifest,
                    'uniqueRef' => $uniqueRef,
                    'commit'    => true,
                ]));

                Yii::$app->session->setFlash('success', 'Consignment queued for dispatch (Ref: ' . $uniqueRef . ').');
                return $this->refresh();
            }

            // Direct send (default)
            try {
                ActionLog::create(['class' => self::class, 'action' => 'create', 'relatedid' => $cart->id, 'type' => 'Info', 'message' => "gforce consignment creation attempt for cart #{$cart->orderref} (Ref: {$uniqueRef})"]);
                $result = $consignmentService->createConsignment($manifest);
                $statusCode = $consignmentService->getStatusCode($result);

                if ($statusCode === 'OK') {
                    $trackingCode = (string) (
                        $result['TrackingCode']
                        ?? $result['Detail']['TrackingCode']
                        ?? $result['Response']['Detail']['TrackingCode']
                        ?? $result['Detail']['ImportDetail'][0]['TrackingCode']
                        ?? $result['Detail']['ImportDetail'][0]['ResponseID']
                        ?? ''
                    );
                    $info       = (string) (
                        $result['Detail']['ImportDetail'][0]['Information']
                        ?? $result['Status']['Description']
                        ?? ''
                    );

                    if ($cart !== null) {
                        $consignmentService->persistCartConsignmentResult($cart, $trackingCode);
                    }

                    Yii::$app->session->setFlash(
                        'success',
                        'Consignment created (Ref: ' . $uniqueRef . '). Tracking Code: '
                        . $trackingCode
                        . ($info ? ' | ' . $info : '')
                    );
                    return $this->refresh();
                }

                $description = $consignmentService->getStatusDescription($result, 'unknown error');

                if (!empty($result['ValidationErrors'])) {
                    $description .= ' Validation: ' . json_encode($result['ValidationErrors'], JSON_UNESCAPED_SLASHES);
                }
                ActionLog::create(['class' => self::class, 'action' => 'create', 'relatedid' => $cart->id, 'type' => 'error', 'message' => "gforce consignment creation attempt failed for cart #{$cart->orderref}"]);
                Yii::$app->session->setFlash('error', 'GForce error: ' . $description);
            } catch (\Exception $e) {
                ActionLog::create(['class' => self::class, 'action' => 'create', 'relatedid' => $cart->id, 'type' => 'error', 'message' => "gforce consignment creation attempt failed for cart #{$cart->orderref}: " . $e->getMessage()]);
                Yii::$app->session->setFlash('error', 'GForce error: ' . $e->getMessage());
            }
        }

        return $this->render('create', [
            'model'          => $model,
            'queueAvailable' => Yii::$app->has('queue'),
        ]);
    }

    /**
     * AJAX: delete a consignment from the customer account (before manifesting).
     *
     * Only works on consignments created with Confirm=no.
     *
     * @return array
     */
    public function actionDelete(): array
    {
        Yii::$app->response->format = Response::FORMAT_JSON;

        $trackingCode = Yii::$app->request->post(
            'trackingCode',
            Yii::$app->request->post('trackingNumber', Yii::$app->request->post('responseId'))
        );

        if (empty($trackingCode)) {
            return ['success' => false, 'message' => 'trackingCode is required.'];
        }

        try {
            $client = Yii::$app->gforceClient;
            $result = $client->deleteConsignment($trackingCode);
            $code   = $result['Status']['Code'] ?? ($result['Response']['Status']['Code'] ?? 'ERROR');
            return [
                'success' => $code === 'OK',
                'message' => $result['Status']['Description'] ?? ($result['Response']['Status']['Description'] ?? ''),
            ];
        } catch (\Exception $e) {
            return ['success' => false, 'message' => $e->getMessage()];
        }
    }

    /**
     * AJAX: manifest (confirm and submit) a pending consignment to the depot.
     *
     * Only works on consignments created with Confirm=no.
     *
     * @return array
     */
    public function actionManifest(): array
    {
        Yii::$app->response->format = Response::FORMAT_JSON;

        $responseId = Yii::$app->request->post('responseId');

        if (empty($responseId)) {
            return ['success' => false, 'message' => 'responseId is required.'];
        }

        try {
            $client = Yii::$app->gforceClient;
            $result = $client->manifestConsignment($responseId);
            $code   = $result['Status']['Code'] ?? ($result['Response']['Status']['Code'] ?? 'ERROR');
            return [
                'success' => $code === 'OK',
                'message' => $result['Status']['Description'] ?? ($result['Response']['Status']['Description'] ?? ''),
            ];
        } catch (\Exception $e) {
            return ['success' => false, 'message' => $e->getMessage()];
        }
    }

    /**
     * AJAX: request ETA data.
     * Supports trackingCode, conNo, customerReference1, customerReference2 or from/to range.
     *
     * @return array
     */
    public function actionEta(): array
    {
        Yii::$app->response->format = Response::FORMAT_JSON;

        $trackingCode = (string) Yii::$app->request->get('trackingCode', Yii::$app->request->get('trackingNumber', ''));
        $conNo = (string) Yii::$app->request->get('conNo', Yii::$app->request->get('consignmentNumber', ''));
        $customerReference1 = (string) Yii::$app->request->get('customerReference1', '');
        $customerReference2 = (string) Yii::$app->request->get('customerReference2', '');
        $from = (string) Yii::$app->request->get('from', '');
        $to = (string) Yii::$app->request->get('to', '');
        $includeAll = filter_var(Yii::$app->request->get('includeAll', false), FILTER_VALIDATE_BOOLEAN);

        try {
            $client = Yii::$app->gforceClient;

            if ($trackingCode !== '') {
                $result = $client->getEtaByTrackingCode($trackingCode, $includeAll);
            } elseif ($conNo !== '') {
                $result = $client->getEtaByConsignmentNumber($conNo, $includeAll);
            } elseif ($customerReference1 !== '') {
                $result = $client->getEtaByCustomerReference1($customerReference1, $includeAll);
            } elseif ($customerReference2 !== '') {
                $result = $client->getEtaByCustomerReference2($customerReference2, $includeAll);
            } elseif ($from !== '' && $to !== '') {
                $result = $client->getEtaByDateRange($from, $to);
            } else {
                return [
                    'success' => false,
                    'message' => 'Provide trackingCode/conNo/customerReference1/customerReference2 or from+to.',
                ];
            }

            $code = $result['Status']['Code'] ?? ($result['Response']['Status']['Code'] ?? 'ERROR');
            return [
                'success' => $code === 'OK',
                'data' => $result['Detail'] ?? ($result['Response']['Detail'] ?? $result),
                'message' => $result['Status']['Description'] ?? ($result['Response']['Status']['Description'] ?? ''),
            ];
        } catch (\Exception $e) {
            return ['success' => false, 'message' => $e->getMessage()];
        }
    }

    /**
     * AJAX: request image references (UID list).
     * Supports trackingCode, conNo, customerReference1, customerReference2 or from/to range.
     *
     * @return array
     */
    public function actionImages(): array
    {
        Yii::$app->response->format = Response::FORMAT_JSON;

        $trackingCode = (string) Yii::$app->request->get('trackingCode', Yii::$app->request->get('trackingNumber', ''));
        $conNo = (string) Yii::$app->request->get('conNo', Yii::$app->request->get('consignmentNumber', ''));
        $customerReference1 = (string) Yii::$app->request->get('customerReference1', '');
        $customerReference2 = (string) Yii::$app->request->get('customerReference2', '');
        $from = (string) Yii::$app->request->get('from', '');
        $to = (string) Yii::$app->request->get('to', '');
        $includePod = filter_var(Yii::$app->request->get('includePod', false), FILTER_VALIDATE_BOOLEAN);
        $includePhoto = filter_var(Yii::$app->request->get('includePhoto', false), FILTER_VALIDATE_BOOLEAN);

        try {
            $client = Yii::$app->gforceClient;

            if ($trackingCode !== '') {
                $result = $client->getImageIdsByTrackingCode($trackingCode, $includePod, $includePhoto);
            } elseif ($conNo !== '') {
                $result = $client->getImageIdsByConsignmentNumber($conNo, $includePod, $includePhoto);
            } elseif ($customerReference1 !== '') {
                $result = $client->getImageIdsByCustomerReference1($customerReference1, $includePod, $includePhoto);
            } elseif ($customerReference2 !== '') {
                $result = $client->getImageIdsByCustomerReference2($customerReference2, $includePod, $includePhoto);
            } elseif ($from !== '' && $to !== '') {
                $result = $client->getImageIdsByDateRange($from, $to);
            } else {
                return [
                    'success' => false,
                    'message' => 'Provide trackingCode/conNo/customerReference1/customerReference2 or from+to.',
                ];
            }

            $code = $result['Status']['Code'] ?? ($result['Response']['Status']['Code'] ?? 'ERROR');
            return [
                'success' => $code === 'OK',
                'data' => $result['Detail'] ?? ($result['Response']['Detail'] ?? $result),
                'message' => $result['Status']['Description'] ?? ($result['Response']['Status']['Description'] ?? ''),
            ];
        } catch (\Exception $e) {
            return ['success' => false, 'message' => $e->getMessage()];
        }
    }

    /**
     * AJAX: request base64 image data by imageType and uId.
     *
     * @return array
     */
    public function actionImageData(): array
    {
        Yii::$app->response->format = Response::FORMAT_JSON;

        $imageType = (string) Yii::$app->request->get('imageType', '');
        $uId = (string) Yii::$app->request->get('uId', Yii::$app->request->get('uid', ''));

        if ($imageType === '' || $uId === '') {
            return ['success' => false, 'message' => 'imageType and uId are required.'];
        }

        try {
            $client = Yii::$app->gforceClient;
            $result = $client->getImageData($imageType, $uId);
            $code = $result['Status']['Code'] ?? ($result['Response']['Status']['Code'] ?? 'ERROR');
            return [
                'success' => $code === 'OK',
                'data' => $result['Detail'] ?? ($result['Response']['Detail'] ?? $result),
                'message' => $result['Status']['Description'] ?? ($result['Response']['Status']['Description'] ?? ''),
            ];
        } catch (\Exception $e) {
            return ['success' => false, 'message' => $e->getMessage()];
        }
    }

    /**
     * AJAX: get tracking notes by customer consignment number.
     *
     * @return array
     */
    public function actionTracking(): array
    {
        Yii::$app->response->format = Response::FORMAT_JSON;

        $trackingCode = Yii::$app->request->get('trackingCode', Yii::$app->request->get('trackingNumber'));
        $conNo = Yii::$app->request->get('conNo');

        if (empty($trackingCode) && empty($conNo)) {
            return ['success' => false, 'message' => 'trackingCode or conNo is required.'];
        }

        try {
            $client = Yii::$app->gforceClient;
            $result = !empty($trackingCode)
                ? $client->getTrackingNotes($trackingCode)
                : $client->getTrackingNotesByConNo($conNo);
            $code   = $result['Status']['Code'] ?? ($result['Response']['Status']['Code'] ?? 'ERROR');
            return [
                'success' => $code === 'OK',
                'data'    => $result['Detail'] ?? ($result['Response']['Detail'] ?? $result),
            ];
        } catch (\Exception $e) {
            return ['success' => false, 'message' => $e->getMessage()];
        }
    }

    /**
     * Retrieve and stream a consignment label PDF by Response ID.
     *
     * @return Response
     */
    public function actionLabels(): Response
    {
        $trackingCode = Yii::$app->request->get(
            'trackingCode',
            Yii::$app->request->get('trackingNumber', Yii::$app->request->get('responseId'))
        );
        $conNo      = Yii::$app->request->get('conNo');
        $response = Yii::$app->response;
    
        try {
            $client = Yii::$app->gforceClient;

            if (!empty($trackingCode)) {
                $pdf      = $client->getLabelsById($trackingCode);
                $filename = 'label-' . $trackingCode . '.pdf';
            } elseif (!empty($conNo)) {
                $pdf      = $client->getLabelsByConNo($conNo);
                $filename = 'label-' . $conNo . '.pdf';
            } else {
                $response->statusCode = 400;
                return $this->asJson([
                    'success' => false,
                    'message' => 'trackingCode/trackingNumber or conNo is required.',
                ]);
            }

            return $response->sendContentAsFile($pdf, $filename, [
                'mimeType' => 'application/pdf',
                'inline'   => true,
            ]);
        } catch (\Exception $e) {
            $response->statusCode = 500;
            return $this->asJson(['success' => false, 'message' => $e->getMessage()]);
        }
    }

    /**
     * AJAX: get available services between two locations.
     *
     * @return array
     */
    public function actionServices(): array
    {
        Yii::$app->response->format = Response::FORMAT_JSON;

        /** @var \PortalPeople\gforce\Module|null $module */
        $module = Yii::$app->getModule('gforce');

        $type       = Yii::$app->request->get('type', 'D');
        $colCountry = Yii::$app->request->get('colCountry', $module ? $module->defaultCollectionCountry : 'GB');
        $colPost    = Yii::$app->request->get('colPostcode', $module ? $module->defaultCollectionPostcode : '');
        $delCountry = Yii::$app->request->get('delCountry', 'GB');
        $delPost    = Yii::$app->request->get('delPostcode', '');

        $colCountry = strtoupper(trim((string) $colCountry));
        $colPost    = strtoupper(trim((string) $colPost));
        $delCountry = strtoupper(trim((string) $delCountry));
        $delPost    = strtoupper(trim((string) $delPost));

        if (empty($colPost) || empty($delPost)) {
            return [
                'success' => false,
                'message' => 'Collection and delivery postcodes are required. '
                    . 'Configure default collection postcode in GForce settings if needed.',
            ];
        }

        try {
            $client = Yii::$app->gforceClient;
            $result = $client->getAvailableServices($type, $colCountry, $colPost, $delCountry, $delPost);
            $code   = $result['Status']['Code'] ?? ($result['Status'][0]['Code'] ?? 'ERROR');
            $description = $result['Status']['Description'] ?? ($result['Status'][0]['Description'] ?? '');
            return [
                'success' => $code === 'OK',
                'data'    => $result['Detail']['Data'] ?? $result,
                'message' => $description,
                'route'   => [
                    'type'       => $type,
                    'colCountry' => $colCountry,
                    'colPost'    => $colPost,
                    'delCountry' => $delCountry,
                    'delPost'    => $delPost,
                ],
            ];
        } catch (\Exception $e) {
            return ['success' => false, 'message' => $e->getMessage()];
        }
    }
    public function actionPickOrder($id) {
        Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;

        $data = [
            'eventType' => 'PICKORDER',
            "snapshot" => [
                "body" => [ 
                    "externalid"=>["value"=>"{$id}","text"=>"{$id}"]
                ]
            ]
        ];

        if ($data === null) {
            Yii::$app->response->statusCode = 400;
            return ['status' => 'error', 'message' => 'Failed to decode payload'];
        }
        try{
            $results = \PortalPeople\netsuite\models\Carts::printLabels($data);
        } catch (\Exception $e) {
            Yii::$app->response->statusCode = 500;
            return [
                'status' => 'error',
                'message' => 'Error processing pick order: ' . $e->getMessage(),
            ];
        }
       
        if ($results === null) {
            Yii::$app->response->statusCode = 500;
            return [
                'status' => 'error',
                'message' => 'No tracking numbers found to print labels for Order. ',
                //'trackinginfo' => $trackingInfo,
            ];
        }
        $base64 = (string) ($results[0]['data'] ?? '');
        if ($base64 === '') {
            throw new \RuntimeException('Provider returned an empty PDF payload.');
        }

        if (strpos($base64, 'base64,') !== false) {
            $parts = explode('base64,', $base64, 2);
            $base64 = $parts[1] ?? $base64;
        }
        
        $pdfBinary = $base64;
        if ($pdfBinary === false) {
            throw new \RuntimeException('label data is not valid Base64 PDF data.');
        }
        Yii::$app->response->statusCode = 200;
        return [
            'status' => 'ok',
            'message' => 'Pick order processed',
            'trackinginfo' => $results[0]['trackingNumbers'] ?? 'No tracking info',
            'body' => $pdfBinary,
        ];
    }
}
