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

namespace PortalPeople\gforce\models;

use yii\base\Model;

/**
 * ConsignmentForm
 *
 * Validates and builds the GForce API manifest JSON payload.
 *
 * API endpoint:  POST https://api-uat.vigocloud.com/api/v1/consignments?code={apiKey}
 * Confirm=yes  → consignment sent directly to depot for processing.
 * Confirm=no   → consignment posted to customer account for manual review.
 *
 * Consignment types:  D=Delivery, C=Collection, 3=Third Party
 * Service codes:      A=Premium Next Day, B=Economy, and others (see AvailableServices API).
 * Bill unit types:    FP=Full (1200kg), LP=Light (750kg), HP=Half (500kg),
 *                     QP=Quarter (250kg), MQP=Mini Quarter (150kg),
 *                     EPL=Euro Pallet (500kg), XLP=XL Pallet (1200kg)
 */
class ConsignmentForm extends Model
{
    // -------------------------------------------------------------------------
    // Consignment core
    // -------------------------------------------------------------------------

    /** @var int Consignment type: 1=Delivery, 2=Collection, 3=ThirdParty. */
    public $consignmentType = 3;

    /** @var string Customer-supplied ID (for batch tracking). Maps to ImportID. */
    public $importId;

    /** @var string Customer consignment number (required – unique per consignment). */
    public $consignmentNumber;

    /** @var string Additional customer reference. */
    public $reference;

    /** @var int Number of pallet lifts (total number of pallets). */
    public $lifts = 1;

    /** @var int Total weight in kg (positive integer). */
    public $weight;

    /** @var bool Handball service required (contents broken down for delivery). */
    public $handball = false;

    /** @var bool Tail lift service required (max 750kg). */
    public $tailLift = false;

    /** @var string|int RSA insurance value (£ per 1000kg). Leave blank for account defaults. */
    public $insurance;

    // -------------------------------------------------------------------------
    // Book-in (required if bookInRequest = true)
    // -------------------------------------------------------------------------

    /** @var bool Book-in service required before delivery. */
    public $bookInRequest = false;

    /** @var string Book-in instructions (required if bookInRequest). */
    public $bookInInstructions;

    /** @var string Book-in reference (required if bookInRequest). */
    public $bookInReference;

    /** @var string Book-in contact name (required if bookInRequest). */
    public $bookInContactName;

    /** @var string Book-in contact phone (required if bookInRequest). */
    public $bookInContactPhone;

    /** @var string Book-in email address. */
    public $bookInEmailAddress;

    // -------------------------------------------------------------------------
    // Delivery notes & dates
    // -------------------------------------------------------------------------

    /** @var string Special delivery instructions (max 255 chars). */
    public $manifestNote;

    /** @var string Collection date YYYY-MM-DD (only for collection service). */
    public $collectionDate;

    /** @var string Requested delivery date YYYY-MM-DD (available on economy service). */
    public $deliveryDate;

    /** @var string Requested delivery time HH:MM:SS (only for timed service). */
    public $deliveryTime;

    // -------------------------------------------------------------------------
    // Service
    // -------------------------------------------------------------------------

    /** @var string Service code (e.g. A=Next Day Premium, B=Economy). */
    public $serviceCode = 'EC';

    /** @var string Service surcharge code. */
    public $serviceSurcharge = null;

    // -------------------------------------------------------------------------
    // Delivery address
    // -------------------------------------------------------------------------

    public $deliveryContactName;
    public $deliveryTelephone;
    public $deliveryCompanyName;
    public $deliveryLine;
    public $deliveryLine2;
    public $deliveryTown;
    public $deliveryCounty;
    public $deliveryPostCode;
    public $deliveryCountry = 'GBR';
    public $deliveryNoteL3;
    public $deliveryNoteL4;

    // -------------------------------------------------------------------------
    // Collection address (optional for D, required for C/3)
    // -------------------------------------------------------------------------

    public $collectionContactName = 'Warehouse';
    public $collectionTelephone = '01952 123456';
    public $collectionCompanyName = 'GForce Express Ltd';
    public $collectionLine = 'Unit D4';
    public $collectionLine2 = 'Tweedale Industrial Estate';
    public $collectionTown = 'Telford';
    public $collectionCounty = 'Shropshire';
    public $collectionPostCode = 'TF7 4JR';
    public $collectionCountry = 'GBR';

    // -------------------------------------------------------------------------
    // Bill unit
    // -------------------------------------------------------------------------

    /** @var string Pallet type: FP, LP, HP, QP, MQP, EPL, XLP. */
    public $billUnitType = 'FP';

    /** @var int Number of billing units. */
    public $billUnitAmount = 1;

    // -------------------------------------------------------------------------
    // Notifications (optional)
    // -------------------------------------------------------------------------

    /**
     * @var string[] Notification groups:
     *   1=Scanned out hub, 2=Scanned in hub, 3=Out for delivery,
     *   4=Delivered, 5=ETA details, 6=Image available,
     *   7=Scheduled AM, 8=Scheduled PM.
     */
    public $notificationSysGroups = [3,5];

    /** @var string SMS number for ETA notifications. */
    public $notificationSms;

    /** @var string Notification email (up to 2 total can be provided). */
    public $notificationEmail;

    /** @var string Second notification email. */
    public $notificationEmail2;

    // -------------------------------------------------------------------------
    // Products (optional)
    // -------------------------------------------------------------------------

    /**
     * @var array Products array – each entry has keys: description, quantity, code.
     *   e.g. [['description' => 'Part A', 'quantity' => 2, 'code' => 'PA001'], ...]
     */
    public $products = [];

    // -------------------------------------------------------------------------

    /** @inheritdoc */
    public function rules(): array
    {
        return [
            // Core consignment
            [['consignmentType'], 'in', 'range' => [1, 2, 3]],
            [['consignmentNumber', 'weight'], 'required'],
            [['lifts'], 'integer', 'min' => 1],
            [['billUnitAmount'], 'integer', 'min' => 1],
            [['weight'], 'integer', 'min' => 1],
            [['handball', 'tailLift', 'bookInRequest'], 'boolean'],
            [['importId', 'consignmentNumber', 'reference'], 'string', 'max' => 100],
            [['deliveryNoteL3', 'deliveryNoteL4'], 'string', 'max' => 48],
            [['manifestNote'], 'string', 'max' => 255],

            // Service
            [['serviceCode', 'serviceSurcharge'], 'required'],
            [['serviceCode', 'serviceSurcharge'], 'string', 'max' => 10],

            // Delivery address
            [['deliveryContactName', 'deliveryTelephone', 'deliveryCompanyName', 'deliveryLine', 'deliveryTown', 'deliveryPostCode'], 'required'],
            [['deliveryContactName', 'deliveryCompanyName'], 'string', 'max' => 50],
            [['deliveryLine', 'deliveryLine2', 'deliveryTown', 'deliveryCounty'], 'string', 'max' => 35],
            [['deliveryTelephone'], 'string', 'max' => 20],
            [['deliveryPostCode'], 'string', 'max' => 10],
            [['deliveryCountry'], 'string', 'length' => 2],

            // Collection address
            [['collectionContactName', 'collectionCompanyName'], 'string', 'max' => 50],
            [['collectionLine', 'collectionLine2', 'collectionTown', 'collectionCounty'], 'string', 'max' => 35],
            [['collectionTelephone'], 'string', 'max' => 20],
            [['collectionPostCode'], 'string', 'max' => 10],
            [['collectionCountry'], 'string', 'length' => 2],
            [['collectionContactName', 'collectionLine', 'collectionTown', 'collectionPostCode'], 'required',
                'when' => function ($model) {
                    return in_array((string) $model->consignmentType, ['C', '3'], true);
                },
                'whenClient' => "function () {\n"
                    . "  var type = $('#consignmentform-consignmenttype').val();\n"
                    . "  return type === 'C' || type === '3';\n"
                    . "}"
            ],

            // Bill unit
            [['billUnitType'], 'in', 'range' => ['FP', 'LP', 'HP', 'QP', 'MQP', 'EPL', 'XLP']],

            // Notifications
            [['notificationEmail', 'notificationEmail2'], 'email'],
            [['notificationSms'], 'string', 'max' => 20],
            [['notificationSysGroups'], 'required', 'when' => function ($model) {
                return !empty($model->notificationSms)
                    || !empty($model->notificationEmail)
                    || !empty($model->notificationEmail2);
            }, 'whenClient' => "function(attribute, value) {\n"
                . "  return $('#consignmentform-notificationsms').val().trim() !== ''\n"
                . "    || $('#consignmentform-notificationemail').val().trim() !== ''\n"
                . "    || $('#consignmentform-notificationemail2').val().trim() !== '';\n"
                . "}"],

            // Dates
            [['collectionDate', 'deliveryDate'], 'date', 'format' => 'php:Y-m-d'],
            [['deliveryTime'], 'match', 'pattern' => '/^\d{2}:\d{2}:\d{2}$/', 'message' => 'Time must be in HH:MM:SS format.'],

            // Book-in conditionals
            [['bookInInstructions', 'bookInReference', 'bookInContactName', 'bookInContactPhone', 'bookInEmailAddress'], 'required', 'when' => function ($model) {
                return (bool) $model->bookInRequest;
            }, 'whenClient' => "function(attribute, value) { return $('#consignmentform-bookinrequest').is(':checked'); }"],

            // Optional text fields
            [['importId', 'reference', 'collectionDate', 'deliveryDate', 'deliveryTime', 'manifestNote',
              'bookInInstructions', 'bookInReference', 'bookInContactName', 'bookInContactPhone',
              'bookInEmailAddress', 'notificationSms', 'notificationEmail', 'notificationEmail2',
              'deliveryLine2', 'deliveryCounty', 'collectionLine2', 'collectionCounty', 'insurance'], 'default', 'value' => ''],
        ];
    }

    /** @inheritdoc */
    public function attributeLabels(): array
    {
        return [
            'consignmentType'      => 'Consignment Type',
            'importId'             => 'Import ID (optional)',
            'consignmentNumber'    => 'Consignment Number',
            'reference'            => 'Customer Reference',
            'lifts'                => 'Number of Lifts (Pallets)',
            'weight'               => 'Total Weight (kg)',
            'handball'             => 'Handball Service Required',
            'tailLift'             => 'Tail Lift Service Required',
            'insurance'            => 'Insurance (£ per 1000kg)',
            'bookInRequest'        => 'Book-In Service Required',
            'bookInInstructions'   => 'Book-In Instructions',
            'bookInReference'      => 'Book-In Reference',
            'bookInContactName'    => 'Book-In Contact Name',
            'bookInContactPhone'   => 'Book-In Contact Phone',
            'bookInEmailAddress'   => 'Book-In Email Address',
            'manifestNote'         => 'Delivery Instructions (max 255 chars)',
            'collectionDate'       => 'Collection Date (YYYY-MM-DD)',
            'deliveryDate'         => 'Delivery Date (YYYY-MM-DD)',
            'deliveryTime'         => 'Delivery Time (HH:MM:SS)',
            'serviceCode'          => 'Service Code',
            'serviceSurcharge'     => 'Surcharge Code',
            'deliveryContactName'  => 'Contact Name',
            'deliveryTelephone'    => 'Telephone',
            'deliveryCompanyName'  => 'Company Name',
            'deliveryLine'         => 'Address Line 1',
            'deliveryLine2'        => 'Address Line 2',
            'deliveryTown'         => 'Town',
            'deliveryCounty'       => 'County',
            'deliveryPostCode'     => 'Post Code',
            'deliveryCountry'      => 'Country (ISO Code)',
            'collectionContactName' => 'Collection Contact Name',
            'collectionTelephone'   => 'Collection Telephone',
            'collectionCompanyName' => 'Collection Company Name',
            'collectionLine'        => 'Collection Address Line 1',
            'collectionLine2'       => 'Collection Address Line 2',
            'collectionTown'        => 'Collection Town',
            'collectionCounty'      => 'Collection County',
            'collectionPostCode'    => 'Collection Post Code',
            'collectionCountry'     => 'Collection Country (ISO Code)',
            'billUnitType'         => 'Pallet Type',
            'billUnitAmount'       => 'Number of Billing Units',
            'notificationSysGroups'=> 'Notification Types',
            'notificationSms'      => 'SMS Number (ETA only)',
            'notificationEmail'    => 'Email Address 1',
            'notificationEmail2'   => 'Email Address 2',
            'products'             => 'Products',
        ];
    }

    // -------------------------------------------------------------------------

    /**
     * Build the full Manifest array for the GForce API.
     *
     * @param bool $confirm  If true, Confirm=yes (auto-confirm to depot).
     * @return array
     */
    public function getManifest(bool $confirm = false): array
    {
        $consignment = [
            'Type'      => $this->consignmentType,
            'ImportID'  => $this->importId ?? '',
            'Number'    => $this->consignmentNumber,
            'Reference' => $this->reference ?? '',
            'Lifts'     => (string) $this->lifts,
            'Weight'    => (string) $this->weight,
            'Handball'  => $this->handball ? 'true' : 'false',
            'TailLift'  => $this->tailLift ? 'true' : 'false',
        ];

        if (!empty($this->insurance)) {
            $consignment['Insurance'] = (string) $this->insurance;
        }

        $consignment['BookInRequest']      = $this->bookInRequest ? 'true' : 'false';
        $consignment['BookInInstructions'] = $this->bookInInstructions ?? '';
        $consignment['BookInReference']    = $this->bookInReference    ?? '';
        $consignment['BookInContactName']  = $this->bookInContactName  ?? '';
        $consignment['BookInContactPhone'] = $this->bookInContactPhone ?? '';
        $consignment['BookInEmailAddress'] = $this->bookInEmailAddress ?? '';
        $consignment['ManifestNote']       = $this->manifestNote       ?? '';
        $consignment['CollectionDate']     = $this->collectionDate     ?? '';
        $consignment['DeliveryDate']       = $this->deliveryDate       ?? '';
        $consignment['DeliveryTime']       = $this->deliveryTime       ?? '';

        $consignment['Service'] = [
            'Type'      => 'Delivery',
            'Code'      => $this->serviceCode,
            'Surcharge' => $this->serviceSurcharge,
        ];

        // Build address lines
        $deliveryAddress = [
            'Type'        => 'Delivery',
            'ContactName' => $this->deliveryContactName,
            'Telephone'   => $this->deliveryTelephone,
            'CompanyName' => $this->deliveryCompanyName,
            'Line'        => $this->deliveryLine,
            'Town'        => $this->deliveryTown,
            'County'      => $this->deliveryCounty ?? '',
            'PostCode'    => $this->deliveryPostCode,
            'Country'     => 'GBR',
        ];

        if (!empty($this->deliveryLine2)) {
            // Secondary address line as additional Line key
            $deliveryAddress['Line2'] = $this->deliveryLine2;
        }

        $addresses = [$deliveryAddress];

        $hasCollectionAddress =
            !empty($this->collectionLine)
            || !empty($this->collectionTown)
            || !empty($this->collectionPostCode)
            || in_array((string) $this->consignmentType, ['C', '3'], true);

        if ($hasCollectionAddress) {
            $collectionAddress = [
                'Type'        => 'Collection',
                'ContactName' => $this->collectionContactName,
                'Telephone'   => $this->collectionTelephone,
                'CompanyName' => $this->collectionCompanyName,
                'Line'        => $this->collectionLine,
                'Town'        => $this->collectionTown,
                'County'      => $this->collectionCounty ?? '',
                'PostCode'    => $this->collectionPostCode,
                'Country'     => 'GBR',
            ];

            if (!empty($this->collectionLine2)) {
                $collectionAddress['Line2'] = $this->collectionLine2;
            }

            $addresses[] = $collectionAddress;
        }

        $consignment['Address'] = $addresses;

        $consignment['BillUnit'] = [
            [
                'Type'   => $this->billUnitType,
                'Amount' => (string) $this->billUnitAmount,
            ],
        ];

        // Notifications
        $sysGroups = array_values(array_filter((array) $this->notificationSysGroups, static function ($value) {
            return $value !== '' && $value !== null;
        }));

        if (!empty($sysGroups)) {
            $notificationSet = [
                'SysGroup' => $sysGroups,
            ];

            if (!empty($this->notificationSms)) {
                $notificationSet['SMSNumber'] = $this->notificationSms;
            }

            $emails = array_values(array_filter([$this->notificationEmail, $this->notificationEmail2]));
            if (!empty($emails)) {
                $notificationSet['Email'] = $emails;
            }

            $consignment['NotificationSet'] = [$notificationSet];
        }

        // Products
        if (!empty($this->products)) {
            $products = [];
            foreach ($this->products as $i => $product) {
                if (empty($product['description'])) {
                    continue;
                }
                $products[] = [
                    'LineNo'      => (string) ($i + 1),
                    'Description' => $product['description'],
                    'Quantity'    => (string) ($product['quantity'] ?? 1),
                    'Code'        => $product['code'] ?? '',
                ];
            }
            if (!empty($products)) {
                $consignment['Product'] = $products;
            }
        }

        // Build a manifest structure matching the GForce/Vigo expected shape
        $manifest = [];

        $manifest['References'] = [
            'CustomerReference' => $this->reference ?? '',
            'CustomerReference2' => $this->importId ?? '',
            'TrackingCode' => (string) time(),
            'ConsignmentType' => $this->consignmentType,
        ];

        // Collection block (use collection* fields)
        $manifest['Collection'] = [
            'Address' => [
                'Name' => $this->collectionCompanyName ?: $this->collectionContactName ?: '',
                'Address1' => $this->collectionLine ?: '',
                'Address2' => $this->collectionLine2 ?: '',
                'Address3' => $this->collectionTown ?: '',
                'Address4' => $this->collectionCounty ?: '',
                'Postcode' => $this->collectionPostCode ?: '',
                'Country' => 'GBR',
                'IsResidential' => false,
            ],
            'Date' => date('Y-m-d', strtotime('+1 day')),
            'Time' => null,
     /*       'Contacts' => [
                [
                    'Name' => $this->collectionContactName ?: '',
                    'Phone' => $this->collectionTelephone ?: '',
                    'SMS' => $this->notificationSms ?: '',
                    'Email' => $this->bookInEmailAddress ?: '',
                ],
                ],
       */
        ];

        // Delivery block
        $manifest['Delivery'] = [
            'Address' => [
                'Name' => $this->deliveryCompanyName ?: $this->deliveryContactName ?: '',
                'Address1' => $this->deliveryLine ?: '',
                'Address2' => $this->deliveryLine2 ?: '',
                'Address3' => $this->deliveryTown ?: '',
                'Address4' => $this->deliveryCounty ?: '',
                'Postcode' => $this->deliveryPostCode ?: '',
                'Country' => 'GBR',
                'IsResidential' => true,
            ],
            'Date' => $this->deliveryDate ?: date('Y-m-d', strtotime('+1 day')),
            'Time' => $this->deliveryTime ?: null,
            'CustomerPaperworkRequired' => (bool) $this->bookInRequest,
            'Contacts' => [
                [
                    'Name' => $this->deliveryContactName ?: '',
                    'Phone' => $this->deliveryTelephone ?: '',
                    'SMS' => $this->notificationSms ?: '',
                    'Email' => $this->notificationEmail ?: '',
                ],
            ],
            'Notes' => [
                'Line1' => $this->manifestNote ?: '',
                'Line2' => '',
                'Line3' => $this->deliveryNoteL3 ?: '',
                'Line4' => $this->deliveryNoteL4 ?: '',
            ],
        ];

        // GoodsDefinition
        $totalSpaces = (int) ($this->billUnitAmount ?? 2);
        $totalKilos = (int) ($this->weight ?? 100);
        // Build GoodsDefinition.Items from bill unit type and products
        $unitMap = [
            'FP' => 'FULL',
            'LP' => 'FULL',
            'HP' => 'HALF',
            'QP' => 'QUARTER',
            'MQP' => 'QUARTER',
            'EPL' => 'FULL',
            'XLP' => 'FULL',
        ];

        $itemType = $unitMap[$this->billUnitType] ?? 'FULL';
        $items = [];

        // Prefer explicit products mapping where provided
        if (!empty($this->products)) {
            foreach ($this->products as $type => $quantity) {
                $items[] = [
                    'ItemType' => $type,
                    'Quantity' => (int) $quantity,
                ];
            }
        }

        $manifest['GoodsDefinition'] = [
            'TotalSpaces' => array_sum(array_column($items, 'Quantity')),
            'TotalKilos' => $totalKilos,
            'UnitCode' => 'K',
            'CubicMetres' => 0,
            'Items' => $items,
            'OversizeCharging' => [
                'Items' => [],
                'TotalSpaces' => 0,
            ],
        ];

        // ServiceDefinition
        $manifest['ServiceDefinition'] = [
            'Service' => $this->serviceCode ?: '',
            'Surcharges' => ["LF", "PB"],
        ];

        // Amazon / Hazardous placeholders
       /* $manifest['Amazon'] = [
            'AdvancedShippingNotification' => '',
            'FulfilmentByAmazon' => '',
            'BillOfLading' => '',
            'CartonCount' => '',
            'UnitCount' => '',
            'PurchaseOrderNumber' => '',
        ];
*/
        //$manifest['HazardousInformation'] = [];

        return $manifest;
    }
}
