<?php

namespace PortalPeople\netsuite\components;

use Yii;
use yii\base\Component;
use yii\base\InvalidConfigException;
use PortalPeople\netsuite\NetSuiteClient;
use backend\models\{ActionLog, Cartsbe, Settings};
/**
 * NetSuite Component with OAuth 2.0 JWT Authentication
 * 
 * This component provides easy access to NetSuite API functionality in Yii2 applications
 * 
 * @package PortalPeople\netsuite\components
 */
class NetSuite extends Component
{
    private static array $productNetSuiteDataCache = [];

    /**
     * @var string NetSuite account ID
     */
    public $accountId;
    
    /**
     * @var string Client ID from NetSuite integration record
     */
    public $clientId;
    
    /**
     * @var string Client secret from NetSuite integration record
     */
    public $clientSecret;
    
    /**
     * @var string Certificate ID (Key ID) from NetSuite
     */
    public $certificateId;
    
    /**
     * @var string Path to private key file for JWT signing
     */
    public $privateKeyPath;
    
    /**
     * @var string NetSuite user internal ID for impersonation
     */
    public $userId;
    
    /**
     * @var string NetSuite REST API base URL (optional, will be auto-generated if not provided)
     */
    public $baseUrl;
    
    /**
     * @var NetSuiteClient NetSuite client instance
     */
    private $_client;
    
    /**
     * @inheritdoc
     */
    public function init()
    {
        parent::init();
        
        if (empty($this->accountId)) {
            throw new InvalidConfigException('NetSuite account ID must be configured');
        }
        
        if (empty($this->clientId)) {
            throw new InvalidConfigException('NetSuite client ID must be configured');
        }
        
        if (empty($this->clientSecret)) {
            throw new InvalidConfigException('NetSuite client secret must be configured');
        }
        
        if (empty($this->certificateId)) {
            throw new InvalidConfigException('NetSuite certificate ID (Key ID) must be configured');
        }
        
        if (empty($this->privateKeyPath)) {
            throw new InvalidConfigException('Private key path must be configured');
        }
        /*
        if (empty($this->userId)) {
            throw new InvalidConfigException('NetSuite user ID must be configured');
        }
        */

        // Resolve private key path relative to module directory if not absolute
        if (!$this->isAbsolutePath($this->privateKeyPath)) {
            $this->privateKeyPath = \Yii::getAlias('@app') . '/' . ltrim($this->privateKeyPath, '/');
        }
    }
    
    /**
     * Check if path is absolute
     * 
     * @param string $path
     * @return bool
     */
    private function isAbsolutePath($path)
    {
        return (PHP_OS_FAMILY === 'Windows') 
            ? preg_match('/^[a-zA-Z]:[\\\\\/]/', $path)
            : strpos($path, '/') === 0;
    }
    
    /**
     * Get NetSuite client instance
     * 
     * @return NetSuiteClient
     */
    public function getClient()
    {
        if ($this->_client === null) {
            $this->_client = new NetSuiteClient([
                'accountId' => $this->accountId,
                'clientId' => $this->clientId,
                'clientSecret' => $this->clientSecret,
                'certificateId' => $this->certificateId,
                'privateKeyPath' => $this->privateKeyPath,
                //'userId' => $this->userId,
                'baseUrl' => $this->baseUrl
            ]);
        }
        
        return $this->_client;
    }
    
    /**
     * Create a new record in NetSuite
     * 
     * @param string $recordType The type of record to create (e.g., 'customer', 'item', 'salesorder')
     * @param array $data Record data
     * @return array Response data
     */
    public function createRecord($recordType, $data)
    {
        return $this->getClient()->post("/record/v1/{$recordType}", $data);
    }
    
    /**
     * Get a record from NetSuite by ID
     * 
     * @param string $recordType The type of record to retrieve
     * @param int $recordId The internal ID of the record
     * @return array Response data
     */
    public function getRecord($recordType, $recordId)
    {
        return $this->getClient()->get("/record/v1/{$recordType}/{$recordId}");
    }
    
    /**
     * Update a record in NetSuite
     * 
     * @param string $recordType The type of record to update
     * @param int $recordId The internal ID of the record
     * @param array $data Updated record data
     * @return array Response data
     */
    public function updateRecord($recordType, $recordId, $data)
    {
        return $this->getClient()->patch("/record/v1/{$recordType}/{$recordId}", $data);
    }
    
    /**
     * Cancel a NetSuite sales order.
     *
     * @param string $salesOrderId The internal ID of the sales order
     * @return array|null Response data or null when NetSuite returns no body
     * @throws \Exception
     */
    public function cancelSalesOrder(string $salesOrderId)
    {
        $endpoint = "/record/v1/salesorder/{$salesOrderId}?_action=cancel";

        try {
            return $this->getClient()->post($endpoint, []);
        } catch (\Exception $e) {
            Yii::warning("NetSuite cancelSalesOrder POST failed for sales order {$salesOrderId}: {$e->getMessage()}", __METHOD__);
            return $this->getClient()->delete($endpoint, []);
        }
    }
    
    /**
     * Delete a record from NetSuite
     * 
     * @param string $recordType The type of record to delete
     * @param int $recordId The internal ID of the record
     * @return array Response data
     */
    public function deleteRecord($recordType, $recordId)
    {
        return $this->getClient()->delete("/record/v1/{$recordType}/{$recordId}");
    }
    
    /**
     * Search for records in NetSuite
     * 
     * @param string $recordType The type of record to search
     * @param array $criteria Search criteria
     * @param array $options Search options (limit, offset, etc.)
     * @return array Response data
     */
    public function searchRecords($recordType, $criteria = [], $options = [])
    {
        $params = [];
        
        if (!empty($criteria)) {
            $params['q'] = json_encode($criteria);
        }
        
        if (!empty($options['limit'])) {
            $params['limit'] = $options['limit'];
        }
        
        if (!empty($options['offset'])) {
            $params['offset'] = $options['offset'];
        }
        
        return $this->getClient()->get("/query/v1/{$recordType}", $params);
    }
    
    /**
     * Execute a SuiteQL query
     * 
     * @param string $query SuiteQL query string
     * @param array $options Query options (limit, offset, etc.)
     * @return array Response data
     */
    public function executeQuery($query, $options = [])
    {
        $params = ['q' => $query];
        
        if (!empty($options['limit'])) {
            $params['limit'] = $options['limit'];
        }
        
        if (!empty($options['offset'])) {
            $params['offset'] = $options['offset'];
        }
        
        return $this->getClient()->get('/query/v1/suiteql', $params);
    }
    
    /**
     * Get record metadata
     * 
     * @param string $recordType The type of record
     * @return array Response data
     */
    public function getRecordMetadata($recordType)
    {
        return $this->getClient()->get("/record/v1/{$recordType}/metadata");
    }

    /**
     * Get metadata catalog for a record type
     *
     * @param string $recordType The record type to retrieve the catalog for (default: 'salesorder')
     * @return array Response data
     */
    public function getMetadataCatalog($recordType = 'salesorder')
    {
        return $this->getClient()->get("/record/v1/metadata-catalog/{$recordType}");
    }

    
    /**
     * Test the connection to NetSuite
     * 
     * @return bool True if connection is successful
     */
    public function testConnection()
    {
        try {
            $this->getClient()->get('/record/v1/account/1');
            return true;
        } catch (\Exception $e) {
            return false;
        }
    }


    public static function GetSendOrderfromNetsuite($id) {
        $array = [];
        $array[] = (\common\components\NetSuiteApi::get('https://' . Yii::$app->params['netsuite']['account_id'] . '.suitetalk.api.netsuite.com/services/rest/record/v1/salesorder/' . $id .'/item/1'))->content;
        $array[] = (\common\components\NetSuiteApi::get('https://' . Yii::$app->params['netsuite']['account_id'] . '.suitetalk.api.netsuite.com/services/rest/record/v1/salesorder/' . $id .'/item/2'))->content;
        $array[] = (\common\components\NetSuiteApi::get('https://' . Yii::$app->params['netsuite']['account_id'] . '.suitetalk.api.netsuite.com/services/rest/record/v1/salesorder/' . $id .'/item/3'))->content;
        return json_encode($array);
    }

    public static function UpdateExistingOrderLines($id): array
    {
        $cart = Cartsbe::find()->where(['id' => $id])->one();
        if (!$cart) {
            throw new \RuntimeException("Cart {$id} not found.");
        }

        $salesOrderId = self::resolveExistingSalesOrderId($cart);
        if (empty($salesOrderId)) {
            Yii::info("Skipping NetSuite order line update for cart {$cart->id}: no linked sales order ID.");

            return [
                'updated' => false,
                'reason' => 'missing-sales-order-id',
            ];
        }

        $payload = [
            'item' => [
                'items' => array_map(
                    static fn($item) => json_decode(json_encode($item), true),
                    self::buildSalesOrderItems($cart)
                ),
            ],
        ];
        $requestedShipDate = self::resolveRequestedShipDate($cart);
        if ($requestedShipDate === null) {
            $requestedShipDate = self::resolveRequestedShipDateFB(4);
            $message = "This Order has no requested ship date, defaulting to next available ship date: {$requestedShipDate}";
            $payload['custbody_exp_ship_date_so'] = $requestedShipDate;
        }
        
        $shippingAddress = $cart->fulladdresses['shipping'] ?? null;
        
        $memo = !empty($shippingAddress->telephone) ? 'TEL:' . $shippingAddress->telephone : '(No Tel Provided)';
        $memo .= !empty($cart->email) ? '    Email:' . $cart->email : '    (No Email Provided)';
        $memo .= !empty($cart->message) ? '    MSG:' . $cart->message : '    (No Message Provided)';
        $memo .= !empty($message) ? '    ' . $message : '';
        $memo .= empty($cart->message) && !empty($cart->delivery_message) ? '    MSG:' . $cart->delivery_message : '    (No Message Provided)';

        $payload['memo'] = $memo;

        
        $payload['custbody_ahs_block_invoicing_so'] = false;

        Yii::$app->netsuiteClient->patch("/record/v1/salesorder/{$salesOrderId}?replace=item", $payload);

        return [
            'updated' => true,
            'salesOrderId' => $salesOrderId,
        ];
    }

    public static function Updateorder_deliver($cart): array
    {
        if (!is_object($cart) && strlen($cart) == 36) {
            $cart = Cartsbe::find()->where(['id' => $cart])->one();
        }
        if (!$cart) {
            throw new \RuntimeException("Cart {$id} not found.");
        }
        $salesOrderId = self::resolveExistingSalesOrderId($cart);
        if (empty($salesOrderId)) {
            Yii::info("Skipping NetSuite order line update for cart {$cart->id}: no linked sales order ID.");
            return [
                'updated' => false,
                'reason' => 'missing-sales-order-id',
            ];
        }
        $payload = [
            'custbody_ahs_block_invoicing_so' => false,
        ];
        if (Yii::$app->has('netsuiteClient')) {
            try{
                $patchResponse = Yii::$app->netsuiteClient->patch("/record/v1/salesorder/{$salesOrderId}", $payload);
                Yii::warning($patchResponse->content, 'NetSuite');
                ActionLog::create(['class' => self::class, 'action' => 'Updateorder_deliver', 'relatedid' => $cart->id, 'type' => 'Info', 'message' => "NetSuite - Updated order #{$salesOrderId} for cart #{$cart->orderref}"]);
            } catch (\Exception $e) {
                Yii::error("NetSuite update order failed for sales order {$salesOrderId}: {$e->getMessage()}", __METHOD__);
                ActionLog::create(['class' => self::class, 'action' => 'Updateorder_deliver', 'relatedid' => $cart->id, 'type' => 'Error', 'message' => "NetSuite - Failed to update order #{$salesOrderId} for cart #{$cart->orderref}: {$e->getMessage()}"]);
                return [
                    'updated' => false,
                    'reason' => 'netsuite-update-failed',
                    'error' => $e->getMessage(),
                    ];
                    }
        } else {
            ActionLog::create(['class' => self::class, 'action' => 'Updateorder_deliver', 'relatedid' => $cart->id, 'type' => 'Error', 'message' => "NetSuite - Failed to update order #{$salesOrderId} for cart #{$cart->orderref}: NetSuite client not available"]);
        }
        return [
            'updated' => true,
            'salesOrderId' => $salesOrderId,
        ];
    }

    public static function SendOrderToNetsuite($cart, $sendPayload = true) {
        if (!is_object($cart) && strlen($cart) == 36) {
            $cart = Cartsbe::find()->where(['id' => $cart])->one();
        }
        $otherRefNum  = match($cart->account_code) {
            'GAR001' => 'TEST ' . $cart->orderref . '  ' . substr($cart->contact, 0,35),
            'QVC001' => 'TEST ' . $cart->orderref . '  ' . substr($cart->contact, 0,35),
            'SCR001' => 'TEST ' . $cart->orderref . '|' . $cart->porderref .'|' . substr($cart->contact, 0,35),
            default => $cart->orderref,
        };
        // Check if order was already sent to NetSuite
        if (!empty($cart->netsuiteid) && $sendPayload === true) {
            $ns = json_decode($cart->netsuiteid, true);
            if (!empty($ns)) {
                \backend\models\ActionLog::create(['class' => self::class, 'action' => 'import', 'relatedid' => $cart->id, 'type' => 'Error', 'message' => "VirtualStock - Sending to Netsuite - order already exists for cart #{$cart->orderref}"]);
                return (object)[
                    'status' => 200,
                    'content' => json_encode(['message' => 'Order already exists in NetSuite', 'netsuite_id' => $ns]),
                    'already_exists' => true
                ];
            }
        }
        
        // Get customer IDs mapping from configuration
        $customerIds = Yii::$app->params['netsuite']['customerids'] ?? [];
        
        // Determine cart source (default to 'default' if not specified)
        $cartSource = 'default';
        
        $account = \backend\models\Accounts::findOne(['account_code' => $cart->account_code]);
        $customerId = $account->intid ?? 24435; // Final fallback to 24435 if no mapping or account found

        $netsuite = new \stdClass();
       // $netsuite->recordtype = 'salesorder';
        $netsuite->entity = new \stdClass();
        $netsuite->entity->id = $customerId;
        $netsuite->externalId = $cart->id; //'/AB';
        $netsuite->email = $cart->email;
        $netsuite->total = 0 + $cart->total;
        $netsuite->source = 'NGauge Commerce';
        $netsuite->tranDate = Date('c', strtotime('now')); //dateTime
        $netsuite->customForm = new \stdClass();
        $netsuite->customForm->id = 100;
        $netsuite->customFieldList = new \stdClass();
        $netsuite->currency = new \stdClass();
        $netsuite->currency->id = 1;
        $netsuite->message = $cart->message; // string;
        $netsuite->balance = $cart->amountout;
        $netsuite->otherRefNum = substr($otherRefNum, 0, 50);
        $netsuite->billingAddress = [
            'addressee' => $cart->fulladdresses['billing']->contact ?? $cart->contact,
            'addr1' => $cart->fulladdresses['billing']->address1,
            'addr2' => $cart->fulladdresses['billing']->address2,
            'city' => substr($cart->fulladdresses['billing']->town, 0 , 50),
            'state' => substr($cart->fulladdresses['billing']->county, 0, 30),
            'country' => 'GB',
            'zip' => $cart->fulladdresses['billing']->postcode,
        ];
        /** @var \backend\models\Address $shippingAddress */
        $shippingAddress = $cart->fulladdresses['shipping'] ?? null;
        $netsuite->shippingAddress = [
            'addressee' => $shippingAddress->contact ?? $cart->contact,
            'addr1' => $shippingAddress->address1,
            'addr2' => $shippingAddress->address2,
            'city' => substr($shippingAddress->town, 0, 50),
            'state' => substr($shippingAddress->county,0 ,30),
            'country' => 'GB',
            'zip' => $shippingAddress->postcode,
            // 'addrPhone' => $shippingAddress->telephone, //No Perms
         ];
        $items = self::buildSalesOrderItems($cart);
        $requestedShipDate = self::resolveRequestedShipDate($cart);
        if ($requestedShipDate === null) {
            $requestedShipDate = self::resolveRequestedShipDateFB(4);
            $message = "This Order has no requested ship date, defaulting to next available ship date: {$requestedShipDate}";
        }
        $netsuite->custbody_exp_ship_date_so = $requestedShipDate;
        //$netsuite->custbody_ahs_order_source = 'NGauge Commerce';
        if (in_array($account->account_code, ['QVC001', 'SCR001'])) {
            $netsuite->custbody_delivery_date_confirmed = true;
        }
        $netsuite->custbody_ahs_block_invoicing_so = false; // string;
        $netsuite->item = new \stdClass();
        $netsuite->item->items = $items;
        $memo = !empty($shippingAddress->telephone) ? 'TEL:' . $shippingAddress->telephone : '(No Tel Provided)';
        $memo .= !empty($cart->email) ? '    Email:' . $cart->email : '    (No Email Provided)';
        $memo .= !empty($cart->message) ? '    MSG:' . $cart->message : '    (No Message Provided)';
        $memo .= !empty($message) ? '    ' . $message : '';
        $memo .= empty($cart->message) && !empty($cart->delivery_message) ? '    MSG:' . $cart->delivery_message : '    (No Message Provided)';

        $netsuite->memo = $memo;
        if ($sendPayload === false || $sendPayload === 'false') {
            return (object)[
                'statusCode' => 200,
                'status' => 200,
                'content' => json_encode($netsuite),
                'isOk' => true
            ];
        }
        try {
            /** @var yii\httpclient\Response $response */
            $response = \common\components\NetSuiteApi::post('https://' . Yii::$app->params['netsuite']['account_id'] . '.suitetalk.api.netsuite.com/services/rest/record/v1/salesOrder', get_object_vars($netsuite));
        } catch (\Exception $e) {
            \backend\models\ActionLog::create(['class' => self::class, 'action' => 'import', 'type' => 'Error', 'message' => 'NetSuite - API request failed: ' . $e->getMessage(), 'jobid' => null, 'relatedid' => $cart->id]);
            return (object)[
                'statusCode' => 400,
                'status' => 400,
                'content' => $e->getMessage(),
                'error' => true
            ];
        }
        
        // Handle response - HttpClient returns statusCode property
        $statusCode = $response->statusCode ?? $response->status ?? 0;
        
        if (!empty($response->isOk) || ((int)$statusCode >= 200 && (int)$statusCode < 300)) {
            \backend\models\ActionLog::create(['class' => self::class, 'action' => 'import', 'type' => 'info', 'message' => 'NetSuite - API request succeeded: ' . $response->content, 'jobid' => null, 'relatedid' => $cart->id]);
            Yii::$app->request->isConsoleRequest ? \yii\helpers\Console::output("Order {$cart->id} successfully sent to NetSuite. Response: " . $response->content) : null;
            Yii::$app->trigger("netsuite.aftersendtonetsuite-{$cart->account_code}", ($event = new \common\events\UserDataEvent(['model' => $cart])));
            $header = $response->getHeaders();
            $locationArray = explode('/', $header['location']);
            $id = $locationArray[array_key_last($locationArray)];
            if (!empty($id)) {
                $cart->refresh();
                $cart->data->netsuite->deliveryid = $id;
                $cart->data->netsuite->last_sync_attempt = date('Y-m-d H:i:s');
                $cart->save(false);
                if ($cart->account_code == 'GAR001') {
                    self::Adddeposit($cart->id, $id, $cart->total, 'Delivery');
                }
                return (object)[
                    'statusCode' => 200,
                    'status' => 200,
                    'content' => $response->content,
                    'isOk' => true
                ];
            }
            // Save NetSuite ID to cart on success
            if (!empty($response->content)) {
                $content = is_string($response->content) ? json_decode($response->content, true) : $response->content;
                if (is_array($content) && !empty($content['id'])) {
                    $netsuiteData = [
                        'id' => $id,
                        'externalId' => $netsuite->externalId,
                        'created_at' => date('Y-m-d H:i:s')
                    ];
                    $cart->save(false);
                    \backend\models\ActionLog::create(['class' => self::class, 'action' => 'import', 'type' => 'info', 'message' => 'NetSuite - API request succeeded: ' . $id, 'jobid' => null, 'relatedid' => $cart->id]);
                    Yii::warning("Order {$cart->orderref} successfully sent to NetSuite. NetSuite ID: {$id}");
                    
                    // Return successful response
                    return (object)[
                        'statusCode' => 200,
                        'status' => 200,
                        'content' => $response->content,
                        'isOk' => true
                    ];
                }
            }
            // Success but no ID in response
            Yii::warning("Order {$cart->id} sent to NetSuite but no ID in response");
            return (object)[
                'statusCode' => 200,
                'status' => 200,
                'content' => $response->content,
                'isOk' => true
            ];
        }
        
        // Handle errors
        $errorContent = is_string($response->content) ? $response->content : json_encode($response->content);
        
        // Check if it's a "already exists" error
        if (strpos($errorContent, 'already exists') !== false) {
            Yii::warning("Order {$cart->id} already exists in NetSuite. ExternalId: {$netsuite->externalId}");
            return (object)[
                'statusCode' => 200,
                'status' => 200,
                'content' => json_encode(['message' => 'Order already exists in NetSuite', 'external_id' => $netsuite->externalId]),
                'already_exists' => true
            ];
        }
        
        \backend\models\ActionLog::create(['class' => self::class, 'action' => 'import', 'type' => 'Error', 'message' => 'NetSuite API Error for cart ' . $cart->id . ': ' . $errorContent, 'jobid' => null, 'relatedid' => $cart->id]);
        Yii::error("NetSuite API Error for cart {$cart->id}: " . $errorContent);
        return $response;
    }

    private static function buildSalesOrderItems($cart): array
    {
        $items = [];

        foreach ($cart->allcartlines as $line) {
            $nsdata = self::resolveLineNetSuiteData($line);
            $items[] = self::buildDeliveryLineItem($line, $cart, $nsdata);

            if ($line->disc > 0) {
                $items[] = self::buildDiscountLineItem($line, $cart);
            }
        }

        return $items;
    }

    private static function resolveRequestedShipDate($cart): ?string
    {
        foreach ($cart->allcartlines as $line) {
            if (!empty($line->requested_date)) {
                return date('c', strtotime($line->requested_date));
            }
        }

        if (!empty($cart->requested_date)) {
            return date('c', strtotime($cart->requested_date));
        }

        return null; 
        }
    private static function resolveRequestedShipDateFB(int $count): string
    {
        $start = self::getNextWorkingDays(4);
        return date('c', strtotime($start[array_key_first($start)]));
        
    }

    private static function resolveExistingSalesOrderId($cart): ?string
    {
        // Ensure cart JSON fields are deserialized into objects
        if ($cart && !is_object($cart->data) && method_exists($cart, 'loadJson')) {
            try {
                $cart->loadJson();
            } catch (\Throwable $_) {
                // ignore
            }
        }

        $netsuiteData = is_object($cart->data) ? ($cart->data->netsuite ?? null) : null;

        foreach (['deliveryid', 'collectionid', 'id'] as $field) {
            $value = null;

            if (is_object($netsuiteData) && isset($netsuiteData->{$field})) {
                $value = $netsuiteData->{$field};
            } elseif (is_array($netsuiteData) && array_key_exists($field, $netsuiteData)) {
                $value = $netsuiteData[$field];
            }

            if (!empty($value)) {
                return (string) $value;
            }
        }

        return null;
    }

    /**
     * Build a NetSuite sales order item from a delivery line.
     *
     * @param object $line
     * @param object $cart
     * @param object|null $nsdata
     * @return \stdClass
     */
    private static function buildDeliveryLineItem($line, $cart, $nsdata): \stdClass
    {
        $item = new \stdClass();
        $itemId = self::readNetSuiteField($nsdata, 'erpid');
        $erpQty = (float) (self::readNetSuiteField($nsdata, 'erp_qty', 1) ?: 1);
        $unitId = self::readNetSuiteField($nsdata, 'unit');
        $supplierId = self::readNetSuiteField($nsdata, 'supplierid');

        $item->externalId = $line->id;
        $item->item = new \stdClass();
        $item->item->id = (int) $itemId;
        $item->item->name = $line->product_name;

        $extraDesc = $line->product_description ?? '';
        $requested_date = $cart->requested_date ?? 'now';

        $item->description = trim($line->product_name . ' ' . substr($line->product_description, 0, 100) . '... ');
        $item->quantity = $line->erpqty;
        $pricePer = $line->price_exvat / ($line->erpqty / $line->qty);
        $item->rate = round($pricePer, 5);
        $item->requestedDate = date('c', strtotime('now'));
        $item->expectedShipDate = date('c', strtotime($requested_date));
        if (!empty($unitId)) {
            $item->units = $unitId;
        }

        $item->custcolweb_qty_sold = $item->quantity;
        $item->custcolweb_description = $item->description;
        $item->custcol_line_id_custom = (string) $line->id;
        $item->custcol10 = (float) $line->cost;
        $item->custcol_poRate = (float) $line->cost;
        $item->porate = (float) $line->cost;
        $item->custcol_po_rate = (float) $line->cost;
        $item->costestimatetype = (string) 'CUSTOM';
        $item->costestimaterate = (float) $line->cost;
        $item->custcol_po_vendor = new \stdClass();
        $item->custcol_po_vendor->id = trim($line->product_name) !== 'Delivery'
            ? 16336
            : $line->haulierint;
            $item->povendor = new \stdClass();
        $item->povendor->id = trim($line->product_name) !== 'Delivery'
            ? 16336
            : $line->haulierint;
        return $item;
    }

    private static function readNetSuiteField($data, string $field, $default = null)
    {
        $data = self::normaliseStructuredData($data);

        if (is_array($data) && array_key_exists($field, $data)) {
            return $data[$field];
        }

        if (is_object($data) && isset($data->{$field})) {
            return $data->{$field};
        }

        return $default;
    }

    private static function readNestedData($data, array $path, $default = null)
    {
        $current = self::normaliseStructuredData($data);

        foreach ($path as $segment) {
            if (is_array($current) && array_key_exists($segment, $current)) {
                $current = self::normaliseStructuredData($current[$segment]);
                continue;
            }

            if (is_object($current) && isset($current->{$segment})) {
                $current = self::normaliseStructuredData($current->{$segment});
                continue;
            }

            return $default;
        }

        return $current;
    }

    private static function normaliseStructuredData($data)
    {
        if (!is_string($data)) {
            return $data;
        }

        $decoded = json_decode($data, true);
        if (json_last_error() === JSON_ERROR_NONE) {
            return $decoded;
        }

        return $data;
    }

    private static function resolveLineNetSuiteData($line)
    {
        $productId = (string) ($line->productid ?? '');
        if ($productId === '') {
            return null;
        }

        if (array_key_exists($productId, self::$productNetSuiteDataCache)) {
            return self::$productNetSuiteDataCache[$productId];
        }

        $rawData = \backend\models\Productsbe::find()
            ->select('data')
            ->where(['id' => $productId])
            ->scalar();

        $nsData = self::readNestedData($rawData, ['netsuite']);
        self::$productNetSuiteDataCache[$productId] = $nsData;

        return $nsData;
    }

    /**
     * Build a NetSuite discount item from a delivery line.
     *
     * @param object $line
     * @param object $cart
     * @return \stdClass
     */
    private static function buildDiscountLineItem($line, $cart): \stdClass
    {
        $item = new \stdClass();
        $item->externalId = $line->id;
        $item->item = new \stdClass();
        $item->item->id = 1592;

        $extraDesc = $line->product_description ?? '';
        $requested_date = $cart->requested_date ?? 'now';

        $item->description = trim($line->product_name . ' ' . $line->product_description . ' ' . $extraDesc);
        $item->quantity = 1;
        $discvat = $line->disc / 1.2;
        $item->rate = 0 - $discvat;
        $item->requestedDate = date('c', strtotime('now'));
        $item->expectedShipDate = date('c', strtotime($requested_date));
        $item->custcolweb_qty_sold = 1;
        $item->custcolweb_description = 'TRUST10 Discount || ' . $line->product_name;
        $item->custcol10 = 0.0;
        $item->custcol_poRate = 0.0;
        $item->custcol_po_rate = 0.0;
        $item->custcol_po_vendor = new \stdClass();
        $item->custcol_po_vendor->id = 16336;

        return $item;
    }

    public static function Adddeposit($id, $netsuiteid, $amount, $type)
    {
        $setting = Settings::findOne(['is_default' => 1]);
        $state = $setting->state;
        $model = Cartsbe::findOne(['id' => $id]);
        $account = \backend\models\Accounts::findOne(['account_code' => $model->account_code]);
		$ns = json_decode($model->netsuiteid, 1);

        $payment = $model->payment;
        $memo = '';
        
        // Determine payment method ID: Stripe/Card=7, PayPal=10
        $paymentMethodId = 7; // Default: Card/Stripe
        
        if ($payment->paymentmethod == 'Card - CreditCard') {
            $memo = 'Card Payment ref: ' . $payment->paymentref . ' Card Shield: ' . $payment->cardno;
        } elseif ($payment->paymentmethod == 'PayPal') {
            $memo = 'Paypal Payment ref: ' . $payment->paymentref;
            $paymentMethodId = 10;
        } elseif ($payment->paymentmethod == 'Stripe') {
            $memo = 'Stripe Payment ref: ' . $payment->paymentref . ' Card Shield: ' . $payment->cardno;
        }
        Yii::warning($memo, $payment->paymentmethod . '-payment-memo');
\backend\models\ActionLog::create(['class' => self::class, 'action' => 'import', 'type' => 'info', 'message' => 'NetSuite Payment - API request started', 'jobid' => null, 'relatedid' => $model->id]);
        // ========== REST API ==========
        $deposit = new \stdClass();

        $deposit->externalId = $model->payment->id . '/' . $type;
        $deposit->customer = new \stdClass();
        $deposit->customer->id = $account->intid;
        $deposit->salesOrder = new \stdClass();
        $deposit->salesOrder->id = $netsuiteid;
        $deposit->payment = $amount;
        $deposit->trandate = date('c', strtotime(str_replace('/','-', $payment->trxdate)));

        $account = new \stdClass();
        $account->id = 1;
        $account->name = '1200 Bank Account - 90101427';
        $deposit->account = $account;

        if (!empty($memo)) {
            $deposit->memo = $memo;
        }

        // Step 1: POST to create the deposit (paymentMethod is not accepted on create)
        $response = \common\components\NetSuiteApi::post(
            'https://4465021.suitetalk.api.netsuite.com/services/rest/record/v1/customerDeposit',
            get_object_vars($deposit)
        );

        $paymentId = null;

        // POST returns 204 with a Location header and no body — extract ID from Location
        if (empty($response->error) && (!isset($response->status) || (int)$response->status < 400)) {
            $headers = $response->getHeaders();
            $location = $headers['location'] ?? null;
            if (!empty($location)) {
                $parts = explode('/', $location);
                $paymentId = $parts[array_key_last($parts)] ?: null;
            }
        }

        if ($paymentId === null) {
            \backend\models\ActionLog::create(['class' => self::class, 'action' => 'import', 'type' => 'info', 'message' => 'NetSuite Payment - API request failed: ' . $response->content, 'jobid' => null, 'relatedid' => $model->id]);
            return ['status' => 100, 'cart_nsid' => null, 'message' => $response->content ?? 'NetSuite error'];
        }

        // Step 2: PATCH to set paymentMethod — REST API ignores it on create but accepts it on update
        try {
            $patchUrl = 'https://4465021.suitetalk.api.netsuite.com/services/rest/record/v1/customerDeposit/' . $paymentId;
            $patchResponse = \common\components\NetSuiteApi::patch($patchUrl, [
                'paymentMethod' => ['id' => $paymentMethodId],
            ]);
            if ($patchResponse->statusCode >= 400) {
                \backend\models\ActionLog::create(['class' => self::class, 'action' => 'import', 'type' => 'info', 'message' => 'NetSuite Payment update - API request succeeded: ' . $response->content, 'jobid' => null, 'relatedid' => $model->id]);
                Yii::warning("Adddeposit: paymentMethod PATCH returned HTTP {$patchResponse->statusCode} for deposit {$paymentId}: " . $patchResponse->content);
            }
        } catch (\Throwable $e) {
            \backend\models\ActionLog::create(['class' => self::class, 'action' => 'import', 'type' => 'info', 'message' => 'NetSuite Payment update - API request failed: ' . $e->getMessage(), 'jobid' => null, 'relatedid' => $model->id]);
            Yii::warning("Adddeposit: paymentMethod PATCH failed for deposit {$paymentId}: " . $e->getMessage());
        }

        if ($paymentId !== null) {
            $payment->updateAttributes(['netsuiteid' => $paymentId]);
        }
        \backend\models\ActionLog::create(['class' => self::class, 'action' => 'import', 'type' => 'info', 'message' => 'NetSuite Payment - API request succeeded', 'jobid' => null, 'relatedid' => $model->id]);        
        return ['status' => 200, 'cart_nsid' => $model->netsuiteid, 'payment_id' => $paymentId, 'message' => 'Saved'];
    }
    /**
     * Return the next $count working days (Mon–Fri) as YYYY-MM-DD strings,
    * starting from tomorrow.
    */
    public static function getNextWorkingDays(int $count): array
    {
        $dates = [];
        $current = new \DateTime('today');
        while (count($dates) < $count) {
            $current->modify('+1 day');
            $dayOfWeek = (int)$current->format('N'); // 1 = Mon, 7 = Sun
            if ($dayOfWeek <= 5) {
                $dates[] = $current->format('Y-m-d');
            }
        }   
        return $dates;
    }
}