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

namespace PortalPeople\gforce\components;

use Yii;
use backend\models\Cartsbe;
use PortalPeople\gforce\models\ConsignmentForm;
use PortalPeople\gforce\Module;
use yii\base\Component;

/**
 * ConsignmentService
 *
 * Contains business logic used by the consignment controller to keep
 * request/response handling separate from GForce workflow logic.
 */
class ConsignmentService extends Component
{
    /**
     * Pre-populate consignment form values from a cart shipping address.
     *
     * @param ConsignmentForm $model
     * @param Cartsbe         $cart
     * @return void
     */
    public function prepopulateModelFromCart(ConsignmentForm $model, ?Module $module, Cartsbe $cart): void
    {
        $client = Yii::$app->gforceClient;
        $shippingAddress = $cart->fulladdresses['shipping'] ?? null;
        if (is_object($shippingAddress)) {
            $country     = strtoupper((string) ($shippingAddress->country ?? ''));
            $countryCode = strlen($country) === 2 ? $country : 'GB';
            
            $model->deliveryContactName = (string) (
                $cart->contact ?? trim($shippingAddress->firstname . ' ' . $shippingAddress->lastname)
            );
            $model->deliveryTelephone   = (string) ($shippingAddress->telephone ?? '');
            $model->deliveryCompanyName = (string) (
                $shippingAddress->company_name ?? $shippingAddress->companyname ?? $model->deliveryContactName
            );
            $model->deliveryLine        = (string) ($shippingAddress->address1 ?? '');
            $model->deliveryLine2       = (string) ($shippingAddress->address2 ?? '');
            $model->deliveryTown        = (string) ($shippingAddress->town ?? '');
            $model->deliveryCounty      = (string) ($shippingAddress->county ?? '');
            $model->deliveryPostCode    = $this->normalizePostcodeWithSpace(
                (string) ($shippingAddress->postcode ?? '')
            );
            $model->deliveryCountry     = $countryCode;
            $model->notificationEmail   = (string) ($shippingAddress->email ?? $cart->email ?? '');
        }

        $deliveryLines  = $cart->deliveryCartlines;
        $lineWeight     = is_array($deliveryLines)
            ? (float) array_sum(array_column($deliveryLines, 'totalweight'))
            : 0.0;
        $fallbackWeight = (float) ($cart->totalweight ?? 0);

        if ($lineWeight > 0) {
            $model->weight = (int) ceil($lineWeight / 1000); // grams -> kg
        } elseif ($fallbackWeight > 0) {
            $model->weight = (int) ceil($fallbackWeight / 1000);
        }

        $model->collectionDate    = date('Y-m-d');
        $model->deliveryDate      = date('Y-m-d', strtotime('+3 days'));
        $model->collectionCountry = strtoupper((string) (($module && $module->defaultCollectionCountry)
            ? $module->defaultCollectionCountry
            : 'GB'));
        $model->collectionPostCode = strtoupper((string) (($module && $module->defaultCollectionPostcode)
            ? $module->defaultCollectionPostcode
            : ''));
        $model->billUnitType      = $cart->deliveryCartlines[0]->product->data->shipping->size_of_pallet == 1.0
            ? 'FP'
            : 'HP';
        $model->consignmentNumber = (string) ($cart->porderref ?? $cart->orderref . '_' . date('His') ?? '');
        // Vigo v1 docs do not expose the route/service lookup endpoint used by
        // the legacy module, so we skip auto-selection here.
    }

    /**
     * Build unique reference used for queue/send feedback.
     *
     * @return string
     */
    public function buildUniqueRef(): string
    {
        return 'NGPW-' . date('YmdHis') . '-' . rand(1000, 9999);
    }

    /**
     * Validate selected service route and run GForce preflight.
     *
     * @param ConsignmentForm $model
     * @param Module|null     $module
     * @param array           $manifest
     * @return array ['ok' => bool, 'message' => string]
     */
    public function validateRouteAndPreflight(ConsignmentForm $model, ?Module $module, array $manifest): array
    {
        $client = Yii::$app->gforceClient;
        $model->deliveryPostCode = $this->normalizePostcodeWithSpace((string) $model->deliveryPostCode);

        $validation = $client->createConsignment($manifest, false);
        Yii::error($validation, 'gforce.validation');

        if ($this->getStatusCode($validation) !== 'OK') {
            $validationDescription = $this->getStatusDescription($validation, 'Validation failed.');
            if (!empty($validation['ValidationErrors'])) {
                $validationDescription .= ' Validation: '
                    . json_encode($validation['ValidationErrors'], JSON_UNESCAPED_SLASHES);
            }

            return [
                'ok' => false,
                'message' => 'GForce validation failed: ' . $validationDescription,
            ];
        }

        return ['ok' => true, 'message' => ''];
    }

    /**
     * Send consignment to GForce as a live import.
     *
     * @param array $manifest
     * @return array
     */
    public function createConsignment(array $manifest): array
    {
        $client = Yii::$app->gforceClient;
        return $client->createConsignment($manifest, true);
    }

    /**
     * Get a status code from either supported GForce response shape.
     *
     * @param array $response
     * @return string
     */
    public function getStatusCode(array $response): string
    {
        return (string) (
            $response['Status']['Code']
            ?? $response['Response']['Status']['Code']
            ?? ($response['Status'][0]['Code'] ?? '')
        );
    }

    /**
     * Get a status description from either supported GForce response shape.
     *
     * @param array  $response
     * @param string $default
     * @return string
     */
    public function getStatusDescription(array $response, string $default = 'unknown error'): string
    {
        return (string) (
            $response['Status']['Description']
            ?? $response['Response']['Status']['Description']
            ?? ($response['Status'][0]['Description'] ?? $default)
        );
    }

    /**
     * Persist successful consignment metadata to cart data payload.
     *
     * @param Cartsbe $cart
     * @param string  $trackingCode
     * @return void
     */
    public function persistCartConsignmentResult(Cartsbe $cart, string $trackingCode): void
    {
        $cartData = is_object($cart->data) ? $cart->data : (object) [];
        if (!isset($cartData->gforce) || !is_object($cartData->gforce)) {
            $cartData->gforce = (object) [];
        }

        $cartData->gforce->response_id   = $trackingCode;
        $cartData->gforce->tracking_number = $trackingCode;
        $cartData->gforce->status        = 'Consignment Created';
        $cartData->gforce->despatch_date = date('Y-m-d H:i:s');

        $cart->data = $cartData;
        $cart->save();
    }

    /**
     * Ensure a postcode contains a single space before the inward code.
     * Example: BN11AA -> BN1 1AA
     *
     * @param string $postcode
     * @return string
     */
    private function normalizePostcodeWithSpace(string $postcode): string
    {
        $compact = strtoupper(preg_replace('/\s+/', '', trim($postcode)) ?? '');
        if ($compact === '' || strlen($compact) <= 3) {
            return $compact;
        }

        return substr($compact, 0, -3) . ' ' . substr($compact, -3);
    }
}
