<?php
// Copyright (c) Pickware GmbH. All rights reserved.
// This file is part of software that is released under a proprietary license.
// You must not copy, modify, distribute, make publicly available, or execute
// its contents or parts thereof without express permission by the copyright
// holder, unless otherwise permitted by law.

namespace Shopware\Plugins\ViisonPickwareCommon\Components;

use Enlight_Components_Db_Adapter_Pdo_Mysql;
use Enlight_Controller_Request_RequestHttp as Request;
use Enlight_Event_EventManager;
use Shopware\Models\Attribute\OrderDetail as OrderDetailAttribute;
use Shopware\Plugins\ViisonPickwareCommon\Classes\Util as PickwareUtil;
use VIISON\AddressSplitter\AddressSplitter;

class RestApiOrdersResourceService
{
    /**
     * @var Enlight_Components_Db_Adapter_Pdo_Mysql
     */
    private $database;

    /**
     * @var Enlight_Event_EventManager
     */
    private $eventManager;

    /**
     * @param Zend_Db_Adapter_Pdo_Abstract $database
     * @param Enlight_Event_EventManager $eventManager
     */
    public function __construct($database, $eventManager)
    {
        $this->database = $database;
        $this->eventManager = $eventManager;
    }

    /**
     * @param array $data
     * @param Request $request
     * @return array
     */
    public function enrichOrderListData(array $data, Request $request)
    {
        if (empty($data)) {
            return;
        }

        // Gather all order ids
        $orderIds = array_map(function ($order) {
            return $order['id'];
        }, $data);
        $orderIdString = implode(',', $orderIds);

        $orderDetails = [];
        // Fetch dispatch information for all orders
        $orderDispatch = $this->database->fetchAll(
            'SELECT o.id AS orderId, d.name
            FROM s_order o
            INNER JOIN s_premium_dispatch d
                ON o.dispatchID = d.id
            WHERE o.id IN (' . $orderIdString . ')'
        );
        foreach ($orderDispatch as $row) {
            $orderDetails[$row['orderId']] = [];
            $orderDetails[$row['orderId']]['dispatchMethod'] = $row['name'];
        }

        // Fetch the billing addresses for all orders
        $orderBilling = $this->database->fetchAll(
            'SELECT b.*, c.countryname AS countryName, c.countryiso AS countryIso, cs.name AS stateName
            FROM s_order_billingaddress b
            LEFT JOIN s_core_countries c
                ON b.countryID = c.id
            LEFT OUTER JOIN s_core_countries_states cs
                ON b.stateID = cs.id
            WHERE b.orderID IN (' . $orderIdString . ')'
        );
        foreach ($orderBilling as $row) {
            $row = $this->convertAddressResult($row);
            $row['id'] = intval($row['id']);
            $orderDetails[$row['orderID']]['billing'] = $row;
            unset($orderDetails[$row['orderID']]['billing']['orderID']);
        }

        // Fetch the shipping addresses for all orders
        $orderShipping = $this->database->fetchAll(
            'SELECT s.*, c.countryname AS countryName, c.countryiso AS countryIso, cs.name AS stateName
            FROM s_order_shippingaddress s
            LEFT JOIN s_core_countries c
                ON s.countryID = c.id
            LEFT OUTER JOIN s_core_countries_states cs
                ON s.stateID = cs.id
            WHERE s.orderID IN (' . $orderIdString . ')'
        );
        foreach ($orderShipping as $row) {
            $row = $this->convertAddressResult($row);
            $row['id'] = intval($row['id']);
            $orderDetails[$row['orderID']]['shipping'] = $row;
            unset($orderDetails[$row['orderID']]['shipping']['orderID']);
        }

        // Fetch the user information for all orders
        $orderUser = $this->database->fetchAll(
            'SELECT o.id as orderId, u.*
            FROM s_order o
            INNER JOIN s_user u
                ON o.userID = u.id
            WHERE o.id IN (' . $orderIdString . ')'
        );
        foreach ($orderUser as $row) {
            $orderDetails[$row['orderId']]['customer'] = [
                'id' => intval($row['id']),
                'number' => $row['customernumber'],
                'email' => $row['email'],
                'phone' => $orderDetails[$row['orderId']]['billing']['phone'],
                'comment' => $row['internalcomment'],
            ];
        }

        // Fetch the payment information for all orders
        $orderPayment = $this->database->fetchAll(
            'SELECT o.id AS orderId, p.*, s.description as paymentStatusDescription
            FROM s_order o
            INNER JOIN s_core_paymentmeans p
                ON o.paymentID = p.id
            INNER JOIN s_core_states s
                ON s.id = o.cleared
            WHERE o.id IN (' . $orderIdString . ')'
        );
        foreach ($orderPayment as $row) {
            $orderDetails[$row['orderId']]['payment'] = [
                'id' => intval($row['id']),
                'name' => $row['name'],
                'description' => $row['description'],
                'statusDescription' => strip_tags($row['paymentStatusDescription']),
            ];
        }

        // Determine extra order details attributes that are required
        $extraOrderDetailAttributes = [];
        if (property_exists(OrderDetailAttribute::class, 'swagCustomizing')) {
            $extraOrderDetailAttributes[] = 'swag_customizing';
        }
        $extraOrderDetailAttributesString = '';
        if (count($extraOrderDetailAttributes) > 0) {
            $extraOrderDetailAttributesString = ', ' . implode(', ', array_map(function ($attribute) {
                return 'oda.' . $attribute;
            }, $extraOrderDetailAttributes));
        }

        // Fetch history of all orders
        $orderHistory = $this->database->fetchAll(
            'SELECT
                h.orderID AS orderId,
                h.id,
                h.userID AS userId,
                a.username AS userName,
                a.name AS name,
                h.previous_order_status_id AS previousOrderStatusId,
                h.order_status_id AS orderStatusId,
                h.previous_payment_status_id AS previousPaymentStatusId,
                h.payment_status_id AS paymentStatusId,
                h.comment,
                h.change_date AS changeDate
            FROM s_order_history h
            INNER JOIN s_core_auth a
                ON h.userID = a.id
            WHERE h.orderID IN (' . $orderIdString . ')
            ORDER BY h.change_date'
        );
        foreach ($orderHistory as $row) {
            $key = $row['orderId'];
            if ($orderDetails[$key]['history'] === null) {
                $orderDetails[$key]['history'] = [];
            }
            unset($row['orderId']);
            $orderDetails[$key]['history'][] = $row;
        }

        // Fetch documents of all orders
        $orderDocuments = $this->database->fetchAll(
            'SELECT
                id,
                docID AS documentNumber,
                orderID AS orderId,
                type AS typeId
            FROM s_order_documents
            WHERE orderID IN (' . $orderIdString . ')'
        );
        foreach ($orderDocuments as $row) {
            $key = $row['orderId'];
            if ($orderDetails[$key]['documents'] === null) {
                $orderDetails[$key]['documents'] = [];
            }
            $orderDetails[$key]['documents'][] = [
                'id' => intval($row['id']),
                'typeId' => intval($row['typeId']),
                'documentNumber' => $row['documentNumber'],
                // Order documents always use a DIN-A4 page size
                'pageSize' => 'DIN-A4',
            ];
        }

        $ordersWithShop = $this->database->fetchAll(
            'SELECT
                `order`.`id` AS `id`,
                `shop`.`id` AS `shopId`,
                `shop`.`name` AS `shopName`
            FROM `s_order` AS `order`
            LEFT JOIN `s_core_shops` AS `shop` ON `order`.`subshopID` = `shop`.id
            WHERE `order`.`id` IN (' . $orderIdString . ')'
        );
        foreach ($ordersWithShop as $orderWithShop) {
            $key = $orderWithShop['id'];
            $orderDetails[$key]['shop'] = [
                'id' => intval($orderWithShop['shopId']),
                'name' => $orderWithShop['shopName'],
            ];
        }

        // Fetch all order details for all orders
        $orderDetailData = PickwareUtil::getRestApiConformOrderDetailData($orderIds);

        $orders = [];
        foreach ($data as $originalOrderData) {
            // Try to find custom data for the order
            $customOrderData = $orderDetails[$originalOrderData['id']];
            if (!$customOrderData) {
                continue;
            }

            // Add the order detail data and calculate the total shipment weight
            $customOrderData['shipmentWeight'] = 0.0;
            $customOrderData['details'] = $orderDetailData[$originalOrderData['id']];
            foreach ($customOrderData['details'] as $detailData) {
                $customOrderData['shipmentWeight'] += $detailData['quantity'] * $detailData['articleDetail']['weight'];
            }

            // Make sure the documents array exists
            if ($customOrderData['documents'] === null) {
                $customOrderData['documents'] = [];
            }

            // Add the order and previously collected details to the list
            $orders[] = array_merge($originalOrderData, $customOrderData);
        }

        // Allow the pickware plugins to augment the orders array with information specific to the individual plugin
        $orders = $this->eventManager->filter(
            'Shopware_Plugins_ViisonPickwareCommon_API_Orders_FilterOrdersResult',
            $orders,
            [
                'request' => $request,
            ]
        );

        return array_values($orders);
    }

    /**
     * Converts the given array representing an billing or shipping address entry to make it ready for a REST API
     * response. That is, it renames some fields to use 'camelCase' and splits the 'street' field using the
     * AddressSplitter, to set 'street', 'streetNumber', 'additional_address_line1' and 'additional_address_line2'
     * correctly. Finally the country ID and name as well as the state ID and name are wrapped in array each.
     *
     * @param array $address the original address array that shall be converted.
     * @return array The converted address array.
     */
    private function convertAddressResult($address)
    {
        // Add camel case to some key
        $replacementKeys = [
            'firstname' => 'firstName',
            'lastname' => 'lastName',
            'zipcode' => 'zipCode',
        ];
        foreach ($replacementKeys as $oldKey => $newKey) {
            $address[$newKey] = $address[$oldKey];
            unset($address[$oldKey]);
        }

        // Prepare the additional address lines
        $additionalAddressLine1 = [
            $address['additional_address_line1']
        ];
        $additionalAddressLine2 = [
            $address['additional_address_line2']
        ];

        // Try to split the 'street' field using the AddressSplitter
        try {
            $result = AddressSplitter::splitAddress($address['street']);
            $address['street'] = $result['streetName'];
            $address['streetNumber'] = $result['houseNumber'];
            $additionalAddressLine1[] = $result['additionToAddress1'];
            $additionalAddressLine2[] = $result['additionToAddress2'];
        } catch (\InvalidArgumentException $e) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement
            // Ignore exception
        }

        // Assemble the additional address lines
        $address['additional_address_line1'] = implode(', ', array_filter($additionalAddressLine1));
        $address['additional_address_line2'] = implode(', ', array_filter($additionalAddressLine2));

        // Wrap country ID and name in an array
        $address['country'] = [
            'id' => intval($address['countryID']),
            'name' => $address['countryName'],
            'isoCode' => $address['countryIso'],
        ];
        unset($address['countryID']);
        unset($address['countryName']);
        unset($address['countryIso']);

        // Wrap state ID and name in array (if present)
        $address['state'] = null;
        if ($address['stateName'] !== null) {
            $address['state'] = [
                'id' => intval($address['stateID']),
                'name' => $address['stateName'],
            ];
        }
        unset($address['stateID']);
        unset($address['stateName']);

        return $address;
    }
}
