<?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\ViisonPickwareMobile\Components\PickingOrderFilter\OrderFilterConditionQueryComponent;

use Shopware\Plugins\ViisonPickwareMobile\Components\PickingOrderFilter\PickingOrderFilterJoinQueryComponentFactory;
use Shopware\Plugins\ViisonPickwareMobile\Components\QueryBuilder\AbstractJoinQueryComponent;
use Shopware\Plugins\ViisonPickwareMobile\Components\QueryBuilder\BooleanCompositionQueryComponent;
use Shopware\Plugins\ViisonPickwareMobile\Components\QueryBuilder\FieldDescriptorQueryComponent;
use Shopware\Plugins\ViisonPickwareMobile\Components\QueryBuilder\GroupByQueryComponent;
use Shopware\Plugins\ViisonPickwareMobile\Components\QueryBuilder\PlainSqlQueryComponent;
use Shopware\Plugins\ViisonPickwareMobile\Components\QueryBuilder\QueryBuilder;
use Shopware\Plugins\ViisonPickwareMobile\Components\QueryBuilder\QueryBuilderJoinResolver;
use Shopware\Plugins\ViisonPickwareMobile\Components\QueryBuilder\QueryComponent;
use Shopware\Plugins\ViisonPickwareMobile\Components\QueryBuilder\QueryComponentFactory;
use Shopware\Plugins\ViisonPickwareMobile\Components\QueryBuilder\SelectQueryComponent;
use Shopware\Plugins\ViisonPickwareMobile\Components\QueryBuilder\SubQueryJoinQueryComponent;

class OrderFilterConditionQueryComponent implements QueryComponent
{
    const ORDER_ITEM_CONDITIONS_TABLE_ALIAS = '__order_item_conditions';

    /**
     * @var QueryComponent
     */
    protected $rootQueryComponent;

    /**
     * @var QueryComponent[]
     */
    protected $orderItemConditions;

    /**
     * @var AbstractJoinQueryComponent[]
     */
    protected $extraJoinQueryComponents;

    /**
     * @param QueryComponent $rootQueryComponent
     * @param QueryComponent[] $orderItemConditions (optional)
     * @param AbstractJoinQueryComponent[] $extraJoinQueryComponents (optional)
     */
    public function __construct(
        QueryComponent $rootQueryComponent,
        array $orderItemConditions = [],
        array $extraJoinQueryComponents = []
    ) {
        $this->rootQueryComponent = $rootQueryComponent;
        $this->orderItemConditions = $orderItemConditions;
        $this->extraJoinQueryComponents = $extraJoinQueryComponents;
    }

    /**
     * @inheritdoc
     */
    public function createQueryString()
    {
        return $this->rootQueryComponent->createQueryString();
    }

    /**
     * @inheritdoc
     */
    public function getRequiredTables()
    {
        return $this->rootQueryComponent->getRequiredTables();
    }

    /**
     * @param string $componentKey
     * @return array
     */
    public function flatMap($componentKey)
    {
        if ($this->rootQueryComponent instanceof BooleanCompositionQueryComponent) {
            return $this->rootQueryComponent->flatMap($componentKey);
        }

        return [$componentKey => $this->rootQueryComponent];
    }

    /**
     * Creates a {@link SubQueryJoinQueryComponent} that selects the results of all order item conditions of the called
     * instance and grouped by `s_order_details.orderID`, which itself is also selected and used as the join key. The
     * rows are filtered by the passed `$orderItemFilterConditions` before grouping.
     *
     * @param QueryComponent[] $orderItemFilterConditions
     * @return SubQueryJoinQueryComponent|null
     */
    public function createOrderItemJoinQueryComponent(array $orderItemFilterConditions)
    {
        if (count($this->orderItemConditions) === 0) {
            return null;
        }

        // Create the base query builder for selecting order item conditions
        $queryBuilder = new QueryBuilder();
        $queryBuilder
            ->select(QueryComponentFactory::createTableFieldSelectWithAlias('s_order_details', 'orderID', 'orderId'))
            ->from('s_order_details')
            ->groupBy(new GroupByQueryComponent(new FieldDescriptorQueryComponent('s_order_details', 'orderID')));

        // Add any order item filter conditions
        if (count($orderItemFilterConditions) > 0) {
            $queryBuilder->where(
                BooleanCompositionQueryComponent::createConjunction(...array_values($orderItemFilterConditions))
            );
        }

        // Select any custom order item conditions (the result of the joined query)
        foreach ($this->orderItemConditions as $conditionAlias => $orderItemConditionQueryComponent) {
            $queryBuilder->addSelect(new SelectQueryComponent($orderItemConditionQueryComponent, $conditionAlias));
        }

        QueryBuilderJoinResolver::addMissingJoinsToQueryBuilder(
            $queryBuilder,
            array_merge(
                PickingOrderFilterJoinQueryComponentFactory::createSimpleJoinComponents(),
                $this->extraJoinQueryComponents
            )
        );

        return new SubQueryJoinQueryComponent(
            AbstractJoinQueryComponent::JOIN_OPERATOR_LEFT_JOIN,
            $queryBuilder->getSql(),
            self::ORDER_ITEM_CONDITIONS_TABLE_ALIAS,
            new PlainSqlQueryComponent(
                sprintf('`%s`.`orderId` = `s_order`.`id`', self::ORDER_ITEM_CONDITIONS_TABLE_ALIAS),
                ['s_order']
            )
        );
    }
}
