<?php

namespace App\Http\Controllers\Network\Classes;

use Carbon\Carbon;
use Exception;
use GuzzleHttp\Client;
use Yajny\Member\Models\Sale;

class AliExpressApi implements SalesGetterApi
{

    public $api_key;
    public $api_secret;
    public $client;
    public $sales;
    public $network_id;
    public $affiliate_id;

    public function __construct($network_id, $affiliate_id, $api_key, $api_secret)
    {
        $this->api_key = $api_key;
        $this->api_secret = $api_secret;
        $this->network_id = $network_id;
        $this->affiliate_id = $affiliate_id;
        $this->client = new Client();
        $this->sales = [];
    }

    /**
     * Getting sales from network and prepare it to be ready for saving.
     *
     * @param DateTime $start_days filter sales based on start days
     * @param DateTime $end_days filter sales based on end days
     *
     * @return array sales orders
     * @throws Exception If issue happened on saving sales
     *
     */
    public function getSales($start_days, $end_days)
    {
        $query = [
            'app_key' => $this->api_key,
            'method' => 'aliexpress.affiliate.order.list',
            'v' => '2.0',
            'partner_id' => 'top-apitools',
            'format' => 'json',
            'sign_method' => 'hmac',
            'force_sensitive_param_fuzzy' => 'true',
            'timestamp' => Carbon::now('Asia/Shanghai')->format('Y-m-d H:i:s'),
            'start_time' => date('Y-m-d', strtotime("today - {$start_days} days ")) . ' 00:00:00',
            'end_time' => date('Y-m-d', strtotime("tomorrow - {$end_days} days ")) . ' 00:00:00',
        ];
        $pendingOrders = $this->getPendingOrders1($query);
        $this->handleSales($pendingOrders, 'pending');
        $pendingOrders = $this->getPendingOrders2($query);
        $this->handleSales($pendingOrders, 'pending');
        $confirmedOrders = $this->getConfirmedOrders($query);
        $this->handleSales($confirmedOrders, 'confirmed');
        $declinedOrders = $this->getDeclinedOrders($query);
        $this->handleSales($declinedOrders, 'declined');
        return $this->sales;
    }

    /**
     * Generating signature for authentication.
     *
     * @param array $query the query parameter before adding signature
     *
     * @return array the query parameter after adding signature
     *
     */
    private function generateSign($query)
    {
        ksort($query);
        $signStr = '';
        foreach ($query as $key => $val) {
            if ('sign' == $key) continue;
            if ($key != '' && $val != '') {
                $signStr .= $key . $val;
            }
        }
        $query['sign'] = strtoupper(hash_hmac('md5', $signStr, $this->api_secret));
        return $query;
    }

    /**
     * Getting pending orders.
     *
     * @param array $query the query parameter
     *
     * @return array the pending sales orders
     *
     */
    private function getPendingOrders1($query)
    {
        $salesOrders = [];
        $total_pages = 1;
        for ($i = 1; $i <= $total_pages; $i++) {
            $query['page_no'] = $i;
            $query['status'] = 'Payment Completed';
            $query = $this->generateSign($query);
            $response = $this->apiCall($query);
            if (is_null($response)) return [];
            $salesOrders = array_merge($salesOrders, $response->orders->order);
            $total_pages = $response->total_page_no;
        }
        return $salesOrders;
    }
    private function getPendingOrders2($query)
    {
        $salesOrders = [];
        $total_pages = 1;
        for ($i = 1; $i <= $total_pages; $i++) {
            $query['page_no'] = $i;
            $query['status'] = 'Buyer Confirmed Receipt';
            $query = $this->generateSign($query);
            $response = $this->apiCall($query);
            if (is_null($response)) return [];
            $salesOrders = array_merge($salesOrders, $response->orders->order);
            $total_pages = $response->total_page_no;
        }
        return $salesOrders;
    }

    /**
     * Getting confirmed orders.
     *
     * @param array $query the query parameter
     *
     * @return array the confirmed sales orders
     *
     */
    private function getConfirmedOrders($query)
    {
        $salesOrders = [];
        $total_pages = 1;

        for ($i = 1; $i <= $total_pages; $i++) {
            $query['page_no'] = $i;
            $query['status'] = 'Completed Settlement';
            $query = $this->generateSign($query);
            $response = $this->apiCall($query);
            if (is_null($response)) return [];
            $salesOrders = array_merge($salesOrders, $response->orders->order);
            $total_pages = $response->total_page_no;
        }
        return $salesOrders;
    }
    /**
     * Getting confirmed orders.
     *
     * @param array $query the query parameter
     *
     * @return array the confirmed sales orders
     *
     */
    private function getDeclinedOrders($query)
    {
        $salesOrders = [];
        $total_pages = 1;

        for ($i = 1; $i <= $total_pages; $i++) {
            $query['page_no'] = $i;
            $query['status'] = 'Invalid';
            $query = $this->generateSign($query);
            $response = $this->apiCall($query);
            if (is_null($response)) return [];
            $salesOrders = array_merge($salesOrders, $response->orders->order);
            $total_pages = $response->total_page_no;
        }
        return $salesOrders;
    }

    /**
     * Calling the network API.
     *
     * @param array $query the query parameter
     *
     * @return object the object response of network API
     *
     */
    private function apiCall($query)
    {
        $salesResponse = $this->client->request(
            'GET',
            'http://api-sg.aliexpress.com/sync',
            [
                'query' => $query
            ]
        );
        $response = json_decode($salesResponse->getBody());
        if (isset($response->error_response)) {
            throw new Exception($response->error_response->msg, $response->error_response->code);
        }
        if (!isset($response->aliexpress_affiliate_order_list_response->resp_result->result)) {
            return null;
        }
        return $response->aliexpress_affiliate_order_list_response->resp_result->result;
    }

    /**
     * preparing sales orders.
     *
     * @param array $salesOrders array of sales order object
     * @param string $status the status of the sales orders
     *
     */
    private function handleSales($salesOrders, $status)
    {
        foreach ($salesOrders as $saleItem) {
            if ($saleItem->tracking_id != $this->affiliate_id) continue;

            if (isset($saleItem->effect_detail_status) && ($saleItem->effect_detail_status == 'FullRefund' || $saleItem->effect_detail_status == 'Antispam'))
                $saleItem->status = 'declined';
            else $saleItem->status = $status;
            if (isset($this->sales[$saleItem->order_id])) {
                if (isset($this->sales[$saleItem->order_id]->items[$saleItem->sub_order_id])) {
                    $this->sales[$saleItem->order_id]->items[$saleItem->sub_order_id]->status = $saleItem->status;
                    $this->sales[$saleItem->order_id]->items[$saleItem->sub_order_id]->sale_amount = $saleItem->finished_amount ?? $saleItem->paid_amount;
                    $this->sales[$saleItem->order_id]->items[$saleItem->sub_order_id]->commission = $saleItem->estimated_finished_commission ?? $saleItem->estimated_paid_commission;
                } else {
                    $this->sales[$saleItem->order_id]->items[$saleItem->sub_order_id] = $saleItem;
                }
            } else {
                if (Sale::where('network_id', $this->network_id)->where('order_id', $saleItem->order_id)->count()) {
                    $saleItem->isNew = false;
                    $this->sales[$saleItem->order_id] = $saleItem;
                    $this->sales[$saleItem->order_id]->items[$saleItem->sub_order_id] = $saleItem;
                } else {
                    $items = [];
                    $existingSale = Sale::where('network_id', $this->network_id)->where('order_id', $saleItem->order_id)->first();
                    if ($existingSale) {
                        $items = $existingSale->items->mapWithKeys(function ($item) {
                            return [
                                $item->sku => (object)[
                                    'sub_order_id' => $item->sku,
                                    'sale_amount' => $item->unit_price,
                                    'commission' => $item->commission,
                                    'status' => $item->status,
                                ]
                            ];
                        });
                    }
                    $this->sales[$saleItem->order_id] = $saleItem;
                    $this->sales[$saleItem->order_id]->isNew = true;
                    if (isset($items[$saleItem->sub_order_id])) {
                        $items[$saleItem->sub_order_id]->sale_amount = $saleItem->finished_amount ?? $saleItem->paid_amount;
                        $items[$saleItem->sub_order_id]->commission = $saleItem->estimated_finished_commission ?? $saleItem->estimated_paid_commission;
                        $items[$saleItem->sub_order_id]->status = $saleItem->status;
                    } else {
                        $items[$saleItem->sub_order_id] = $saleItem;
                    }
                    $this->sales[$saleItem->order_id]->items = $items;
                }
            }
        }
    }
}
