<?php

namespace PortalPeople\virtualstock\services;

use yii\base\BaseObject;
use PortalPeople\virtualstock\components\VirtualStockClient;
use PortalPeople\virtualstock\constants\ApiEndpoints;
use PortalPeople\virtualstock\models\Order;

class OrderService extends BaseObject
{
    public VirtualStockClient $client;

    /**
     * List orders with optional filters.
     *
     * @param array{
     *   supplier_id?: string,
     *   retailer_id?: string,
     *   order_reference?: string,
     *   part_number?: string,
     *   status?: string,
     *   limit?: int,
     *   offset?: int
     * } $filters
     * @return array{results: Order[], count: int, next: string|null, previous: string|null}
     */
    public function listOrders(array $filters = []): array
    {
        $query = array_filter($filters, fn($v) => $v !== null && $v !== '');
        $query['format'] = 'json';

        $response = $this->client->request('GET', ApiEndpoints::ORDERS, $query);

        $orders = [];
        foreach ($response['results'] ?? [] as $item) {
            $orders[] = Order::fromArray($item);
        }

        return [
            'results'  => $orders,
            'count'    => (int) ($response['count'] ?? count($orders)),
            'next'     => $response['next'] ?? null,
            'previous' => $response['previous'] ?? null,
        ];
    }

    /**
     * Retrieve a single order by its ID/URI.
     */
    public function getOrder(string $orderId): Order
    {
        $path = ApiEndpoints::resolve(ApiEndpoints::ORDER, $orderId);
        $response = $this->client->request('GET', $path, ['format' => 'json']);
        return Order::fromArray($response);
    }

    /**
     * Acknowledge an order and provide expected dispatch/delivery dates per line.
     *
     * Each item in $lines must contain:
     *   - part_number (string)
     *   - line_ref (string)
     *   - quantity (int)
     *   - sub_status ('Pending' or 'Booked' — Screwfix requires 'Booked')
     *   - supplier_dispatch_date (ISO 8601 datetime string)
     *   - supplier_delivery_date (ISO 8601 datetime string)
     *
     * @param array<int, array<string, mixed>> $lines
     */
    public function acknowledgeOrder(string $orderId, array $lines): array
    {
        $path = ApiEndpoints::resolve(ApiEndpoints::ORDER_ACKNOWLEDGE, $orderId);
        return $this->client->request('POST', $path, ['items' => $lines]);
    }

    /**
     * Mark an order as dispatched.
     *
     * @param array<string, mixed> $payload
     */
    public function dispatchOrder(string $orderId, array $payload = []): array
    {
        $path = ApiEndpoints::resolve(ApiEndpoints::ORDER_DISPATCH, $orderId);
        return $this->client->request('POST', $path, $payload);
    }
    /**
     * Mark an order as NGauge dispatched.
     *
     * @param array<string, mixed> $payload
     */
    public static function NGaugedispatchOrder($cart): array
    {
        $vsService = \Yii::createObject(\PortalPeople\virtualstock\services\VirtualStockService::class);
        $module = \Yii::$app->getModule('virtualstock');
        $vsService->client = $module->createClient();
        $vsService->orderService = \Yii::createObject(\PortalPeople\virtualstock\services\OrderService::class);
        $resp = $vsService->dispatchOrderFromCart($cart);
        return ['success' => true, 'response' => $resp];
    }

    /**
     * Normalize and validate an incoming update payload from controller.
     * Returns sanitized payload array or throws \InvalidArgumentException on validation error.
     *
     * @param mixed $payload
     * @return array
     */
    public function prepareUpdatePayload($payload): array
    {
        $payloadData = [];
        if (is_string($payload) && $payload !== '') {
            $decoded = json_decode($payload, true);
            if (json_last_error() !== JSON_ERROR_NONE) {
                throw new \InvalidArgumentException('Invalid JSON payload: ' . json_last_error_msg());
            }
            if (is_array($decoded)) {
                $payloadData = $decoded;
            }
        } elseif (is_array($payload)) {
            $payloadData = $payload;
        }

        if (isset($payloadData['items'])) {
            if (!is_array($payloadData['items'])) {
                throw new \InvalidArgumentException('Invalid payload: "items" must be an array');
            }
            foreach ($payloadData['items'] as $idx => $item) {
                if (!is_array($item)) {
                    throw new \InvalidArgumentException("Invalid payload: items[{$idx}] must be an object");
                }
                if (empty($item['part_number']) || !isset($item['quantity'])) {
                    throw new \InvalidArgumentException("Invalid payload: items[{$idx}] missing required fields (part_number, quantity)");
                }
                if (!is_int($item['quantity'])) {
                    if (is_numeric($item['quantity'])) {
                        $payloadData['items'][$idx]['quantity'] = (int)$item['quantity'];
                    } else {
                        throw new \InvalidArgumentException("Invalid payload: items[{$idx}].quantity must be an integer");
                    }
                }
            }
        }

        return $payloadData;
    }

    /**
     * Build acknowledge payload items array from a Cart model instance.
     * @param \PortalPeople\virtualstock\models\Cart $cart
     * @return array{items: array}
     */
    public function buildAcknowledgePayloadFromCart($cart): array
    {
        $items = [];
        foreach ($cart->deliveryCartlines as $line) {
            $data = json_decode($line->data ?? '{}', true) ?: [];
            $items[] = [
                'part_number' => $data['part_number'] ?? null,
                'line_ref' => $data['line_reference'] ?? null,
                'quantity' => isset($line->qty) ? (int)$line->qty : null,
                'sub_status' => 'Booked',
                'supplier_dispatch_date' => isset($cart->shippingline->requested_date) ? substr((string)$cart->shippingline->requested_date, 0, 19) : date('Y-m-d\TH:i:s'),
                'supplier_delivery_date' => isset($cart->shippingline->requested_date) ? substr((string)$cart->shippingline->requested_date, 0, 19) : date('Y-m-d\TH:i:s'),
            ];
        }
        return ['items' => $items];
    }

    /**
     * Build dispatch payload items array from a Cart model instance.
     * @param \PortalPeople\virtualstock\models\Cart $cart
     * @return array{items: array}
     */
    public function buildDispatchPayloadFromCart($cart): array
    {
        $items = [];
        foreach ($cart->cartlines as $line) {
            $data = json_decode($line->data ?? '{}', true) ?: [];
            $items[] = [
                'part_number' => $data['part_number'] ?? null,
                'line_ref' => $data['line_reference'] ?? null,
                'quantity' => isset($line->qty) ? (int)$line->qty : null,
                'tracking_number' => null,
                'carrier' => null,
                'supplier_dispatch_date' => isset($cart->shippingline->requested_date) ? substr((string)$cart->shippingline->requested_date, 0, 10) : null,
                'supplier_delivery_date' => isset($cart->shippingline->requested_date) ? substr((string)$cart->shippingline->requested_date, 0, 10) : null,
            ];
        }
        return ['items' => $items];
    }

    /**
     * Acknowledge a cancellation request.
     */
    public function cancelOrder(string $orderId, array $payload = []): array
    {
        $path = ApiEndpoints::resolve(ApiEndpoints::ORDER_CANCEL, $orderId);
        return $this->client->request('POST', $path, $payload);
    }

    /**
     * Paginate through all orders matching $filters, yielding Order objects.
     * Calls listOrders() repeatedly until there is no 'next' page.
     *
     * @return \Generator<Order>
     */
    public function iterateOrders(array $filters = []): \Generator
    {
        $filters['offset'] = $filters['offset'] ?? 0;
        $filters['limit']  = $filters['limit'] ?? 100;

        do {
            $page = $this->listOrders($filters);
            foreach ($page['results'] as $order) {
                yield $order;
            }
            $hasNext = $page['next'] !== null;
            $filters['offset'] += $filters['limit'];
        } while ($hasNext);
    }
}
