<?php

namespace domain\Distribution\Distribution;

use domain\Producer\Producer\ProducerSolver;
use domain\Subscription\Subscription\Subscription;
use domain\Subscription\Subscription\SubscriptionSolver;
use domain\_\AbstractRepository;

class DistributionRepository extends AbstractRepository
{
    protected DistributionRepositoryQuery $query;
    protected DistributionSolver $distributionSolver;
    protected SubscriptionSolver $subscriptionSolver;
    protected ProducerSolver $producerSolver;

    public function loadDependencies(): void
    {
        $this->loadQuery(DistributionRepositoryQuery::class);
        $this->distributionSolver = $this->loadService(DistributionSolver::class);
        $this->subscriptionSolver = $this->loadService(SubscriptionSolver::class);
        $this->producerSolver = $this->loadService(ProducerSolver::class);
    }

    public function getDefaultOptionsSearch(): array
    {
        return [
            self::WITH => [],
            self::JOIN_WITH => [],
            self::ORDER_BY => 'date ASC',
            self::ATTRIBUTE_ID_PRODUCER => 'distribution.id_producer'
        ];
    }

    public function findOneDistributionById(int $id): ?Distribution
    {
        return $this->createQuery()
            ->filterById($id)
            ->findOne();
    }

    public function findOneDistribution(string $date, bool $active = null): ?Distribution
    {
        $query = $this->createDefaultQuery();
        $query->filterByDate($date);
        if(!is_null($active)) {
            $query->filterByActive($active);
        }
        return $query->findOne();
    }

    public function findOneFirstDistribution(): ?Distribution
    {
        return $this->createDefaultQuery()
            ->orderBy('date ASC')
            ->findOne();
    }

    public function findOneLastDistribution(): ?Distribution
    {
        return $this->createDefaultQuery()
            ->orderBy('date DESC')
            ->findOne();
    }

    public function findDistributionsActive(): array
    {
        return $this->createDefaultQuery()
            ->filterByActive(true)
            ->orderBy('date ASC')
            ->find();
    }

    public function findDistributionsWithOrders()
    {
        return $this->createDefaultQuery()
            ->filterHasOrders()
            ->orderBy('date ASC')
            ->find();
    }

    /**
     * Récupère les distributions futures.
     */
    public function findDistributionsIncoming($ignoreActive = false): array
    {
        $query = $this->createDefaultQuery();

        if(!$ignoreActive) {
            $query->filterByActive(true);
        }

        $query
            ->filterByDateBetterThanToday()
            ->orderBy('date ASC');

        return $query->find();
    }

    /**
     * Recherche les distributions futures où l'abonnement peut s'appliquer.
     */
    public function findDistributionsIncomingMatchWithSubscrtiption(Subscription $subscription, bool $filterByOrderDeadline = false): array
    {
        $incomingDistributionsArray = $this->createDefaultQuery()
            ->filterByDateBetterThanToday()
            ->filterBySubscriptionDates($subscription)
            ->find();

        if($filterByOrderDeadline) {
            $incomingDistributionsArray = $this->distributionSolver
                ->filterDistributionsByDateDelay($incomingDistributionsArray);
        }

        $matchedIncomingDistributionsArray = [];
        foreach ($incomingDistributionsArray as $incomingDistribution) {
            if ($this->subscriptionSolver->isSubscriptionMatchWith($subscription, $incomingDistribution->date)) {
                $matchedIncomingDistributionsArray[] = $incomingDistribution;
            }
        }

        return $matchedIncomingDistributionsArray;
    }

    public function isDistributionDateAvailable(string $date = null): bool
    {
        if($date && strlen($date)) {
            $distribution = $this->findOneDistribution($date);
            if($distribution && $this->distributionSolver->isDistributionAvailable($distribution)) {
                return true;
            }
        }
        
        return false;
    }

    public function isOneDistributionWeekActive(string $date): bool
    {
        $oneDistributionWeekActive = false;
        $week = sprintf('%02d', date('W', strtotime($date)));
        $start = strtotime(date('Y', strtotime($date)) . 'W' . $week);
        $dateMonday = date('Y-m-d', strtotime('Monday', $start));
        $dateTuesday = date('Y-m-d', strtotime('Tuesday', $start));
        $dateWednesday = date('Y-m-d', strtotime('Wednesday', $start));
        $dateThursday = date('Y-m-d', strtotime('Thursday', $start));
        $dateFriday = date('Y-m-d', strtotime('Friday', $start));
        $dateSaturday = date('Y-m-d', strtotime('Saturday', $start));
        $dateSunday = date('Y-m-d', strtotime('Sunday', $start));

        $weekDistribution = Distribution::find()
            ->andWhere([
                'id_producer' => $this->getProducerContextId(),
                'active' => 1,
            ])
            ->andWhere([
                'or',
                ['date' => $dateMonday],
                ['date' => $dateTuesday],
                ['date' => $dateWednesday],
                ['date' => $dateThursday],
                ['date' => $dateFriday],
                ['date' => $dateSaturday],
                ['date' => $dateSunday],
            ])
            ->one();
        if ($weekDistribution) {
            $oneDistributionWeekActive = true;
        }

        return $oneDistributionWeekActive;
    }

    public function findDistributionsDashboard(): array
    {
        $optionDashboardNumberDistributions = $this->producerSolver->getConfig('option_dashboard_number_distributions');
        $optionDashboardDateStart = $this->producerSolver->getConfig('option_dashboard_date_start');
        $optionDashboardDateEnd = $this->producerSolver->getConfig('option_dashboard_date_end');
        $dashboardNumberDistributions = $optionDashboardNumberDistributions ?: 3;

        $queryDistributions = Distribution::find()->with('order');

        if ($optionDashboardDateStart || $optionDashboardDateEnd) {
            if ($optionDashboardDateStart) {
                $queryDistributions->andWhere(['>=', 'distribution.date', $optionDashboardDateStart]);
            }
            if ($optionDashboardDateEnd) {
                $queryDistributions->andWhere(['<=', 'distribution.date', $optionDashboardDateEnd]);
            }
        } else {
            $queryDistributions->andWhere(['>=', 'distribution.date', date('Y-m-d')]);
        }

        $distributionsArray = $queryDistributions->andWhere([
            'distribution.id_producer' => $this->getProducerContextId(),
            'distribution.active' => 1
        ])
            ->orderBy('date ASC')
            ->limit($dashboardNumberDistributions)
            ->all();

        return $distributionsArray;
    }
}