<?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 DateInterval;
use DateTime;
use Shopware\Plugins\ViisonPickwareMobile\Components\QueryBuilder\ComparisonQueryComponent;
use Shopware\Plugins\ViisonPickwareMobile\Components\QueryBuilder\FunctionQueryComponent;
use Shopware\Plugins\ViisonPickwareMobile\Components\QueryBuilder\ScalarValueQueryComponent;
use Shopware\Plugins\ViisonPickwareMobile\Components\QueryComponentArrayCoding\ComparisonQueryComponentArrayCoder;
use Shopware\Plugins\ViisonPickwareMobile\Components\QueryComponentArrayCoding\FieldDescriptorQueryComponentArrayCoder;
use Shopware\Plugins\ViisonPickwareMobile\Components\QueryComponentArrayCoding\QueryComponentArrayCodingService;
use Shopware\Plugins\ViisonPickwareMobile\Components\QueryComponentArrayCoding\QueryComponentArrayDecodingPreprocessor;
use Shopware\Plugins\ViisonPickwareMobile\Components\QueryComponentArrayCoding\ScalarValueQueryComponentArrayCoder;
use Shopware\Plugins\ViisonPickwareMobile\Components\QueryComponentArrayCoding\UnaryOperationQueryComponentArrayCoder;

class DateOperatorQueryComponentArrayDecodingPreprocessor implements QueryComponentArrayDecodingPreprocessor
{
    /**
     * @var string
     */
    const IS_TODAY = 'is_today';
    const WAS_DAYS_AGO = 'was_days_ago';
    const WAS_MIN_DAYS_AGO = 'was_min_days_ago';
    const WAS_MAX_DAYS_AGO = 'was_max_days_ago';
    const IS_IN_DAYS = 'is_in_days';
    const IS_IN_MIN_DAYS = 'is_in_min_days';
    const IS_IN_MAX_DAYS = 'is_in_max_days';

    /**
     * @var string[]
     */
    const PROCESSABLE_DATE_OPERATORS = [
        self::WAS_DAYS_AGO,
        self::WAS_MAX_DAYS_AGO,
        self::WAS_MIN_DAYS_AGO,
        self::IS_IN_DAYS,
        self::IS_IN_MIN_DAYS,
        self::IS_IN_MAX_DAYS,
    ];

    /**
     * @var QueryComponentArrayCodingService
     */
    protected $codingService;

    /**
     * @param QueryComponentArrayCodingService $codingService
     */
    public function __construct(QueryComponentArrayCodingService $codingService)
    {
        $this->codingService = $codingService;
    }

    /**
     * {@inheritdoc}
     *
     * Processes the operators, convert them to valid SQL and calculate the date for the given day difference.
     */
    public function process(array $arrayData)
    {
        $componentDataMatches = (
            isset($arrayData['type'])
            && (
                (
                    $arrayData['type'] === ComparisonQueryComponentArrayCoder::CODABLE_TYPE
                    && in_array($arrayData['operator'], self::PROCESSABLE_DATE_OPERATORS)
                    && isset($arrayData['rightOperand']['type'])
                    && $arrayData['leftOperand']['type'] === FieldDescriptorQueryComponentArrayCoder::CODABLE_TYPE
                    && $arrayData['rightOperand']['type'] === ScalarValueQueryComponentArrayCoder::CODABLE_TYPE
                )
                || (
                    $arrayData['type'] === UnaryOperationQueryComponentArrayCoder::CODABLE_TYPE
                    && $arrayData['operator'] === self::IS_TODAY
                    && $arrayData['leftOperand']['type'] === FieldDescriptorQueryComponentArrayCoder::CODABLE_TYPE
                )
            )
        );
        if (!$componentDataMatches) {
            return $arrayData;
        }

        $comparisonDate = new DateTime();
        if ($arrayData['operator'] === self::WAS_DAYS_AGO
            || $arrayData['operator'] === self::WAS_MIN_DAYS_AGO
            || $arrayData['operator'] === self::WAS_MAX_DAYS_AGO
        ) {
            $comparisonDate = $comparisonDate->sub(new DateInterval('P' . $arrayData['rightOperand']['value'] . 'D'));
        } elseif ($arrayData['operator'] === self::IS_IN_DAYS
            || $arrayData['operator'] === self::IS_IN_MIN_DAYS
            || $arrayData['operator'] === self::IS_IN_MAX_DAYS
        ) {
            $comparisonDate = $comparisonDate->add(new DateInterval('P' . $arrayData['rightOperand']['value'] . 'D'));
        }

        switch ($arrayData['operator']) {
            case self::IS_TODAY:
            case self::IS_IN_DAYS:
            case self::WAS_DAYS_AGO:
                $operator = '=';
                break;
            case self::IS_IN_MAX_DAYS:
            case self::WAS_MIN_DAYS_AGO:
                $operator = '<=';
                break;
            case self::IS_IN_MIN_DAYS:
            case self::WAS_MAX_DAYS_AGO:
                $operator = '>=';
                break;
        }

        $queryComponent = new ComparisonQueryComponent(
            new FunctionQueryComponent('DATE', $this->codingService->decode($arrayData['leftOperand'])),
            $operator,
            new ScalarValueQueryComponent($comparisonDate->format('Y-m-d'))
        );

        return $this->codingService->encode($queryComponent);
    }
}
