<?php

namespace common\logic\Producer\Producer\Repository;

use common\helpers\Departments;
use common\helpers\Price;
use common\logic\AbstractRepository;
use common\logic\Document\Document\Model\DocumentInterface;
use common\logic\Document\Document\Service\DocumentSolver;
use common\logic\PointSale\PointSale\Model\PointSale;
use common\logic\Producer\Producer\Model\Producer;
use common\logic\Producer\Producer\Service\ProducerSolver;
use common\logic\Producer\ProducerPriceRange\Repository\ProducerPriceRangeRepository;
use common\logic\User\User\Model\User;
use yii\helpers\Html;

class ProducerRepository extends AbstractRepository
{
    protected ProducerRepositoryQuery $query;
    protected ProducerPriceRangeRepository $producerPriceRangeRepository;
    protected ProducerSolver $producerSolver;
    protected DocumentSolver $documentSolver;

    public function loadDependencies(): void
    {
        $this->loadQuery(ProducerRepositoryQuery::class);
        $this->producerPriceRangeRepository = $this->loadService(ProducerPriceRangeRepository::class);
        $this->producerSolver = $this->loadService(ProducerSolver::class);
        $this->documentSolver = $this->loadService(DocumentSolver::class);
    }

    /**
     * Retourne les options de base nécessaires à la fonction de recherche.
     *
     */
    public function getDefaultOptionsSearch(): array
    {
        return [
            self::WITH => ['taxRate', 'contact'],
            self::JOIN_WITH => [],
            self::ORDER_BY => 'name ASC',
            self::ATTRIBUTE_ID_PRODUCER => 'id'
        ];
    }

    public function findOneProducerById(int $id)
    {
        return $this->createQuery()
            ->filterById($id)
            ->findOne();
    }

    public function findOneProducerBySlug(string $slug)
    {
        return $this->createQuery()
            ->filterBySlug($slug)
            ->findOne();
    }

    public function queryProducersActive()
    {
        return $this->createQuery()
            ->filterIsActive()
            ->orderBy('producer.name ASC');
    }

    /**
     * Retourne le compte producteur de démonstration.
     */
    public function findOneProducerDemoAccount()
    {
        return $this->createQuery()
            ->filterIsDemoAccount()
            ->findOne();
    }

    /**
     * Retourne la liste des établissements pour l'initialisation d'une listesélective.
     */
    public function populateProducerDropdown(): array
    {
        $producers = $this->createQuery()
            ->filterIsActive()
            ->orderBy('postcode, city ASC')
            ->find();

        $departments = Departments::get();
        $dataProducers = [];
        $optionsProducers = [];

        foreach ($producers as $p) {
            $departmentCode = substr($p->postcode, 0, 2);
            if (!key_exists('d' . $departmentCode, $dataProducers) && isset($departments[$departmentCode])) {
                $dataProducers['d' . $departmentCode] = '<strong>' . $departments[$departmentCode] . '</strong>';
                $optionsProducers['d' . $departmentCode] = ['disabled' => true];
            }

            $dataProducers[$p->id] = '<span class="glyphicon glyphicon-lock"></span> ' . Html::encode(
                    $p->name
                ) . ' - ' . Html::encode($p->postcode) . ' ' . Html::encode(
                    $p->city
                ) . ' <span class="glyphicon glyphicon-lock"></span>';

            if (strlen($p->code)) {
                $optionsProducers[$p->id] = ['class' => 'lock'];
            }
        }

        return [
            'data' => $dataProducers,
            'options' => $optionsProducers
        ];
    }

    public function getTurnoverLastMonth(Producer $producer, bool $format = false)
    {
        $period = date('Y-m', strtotime('-1 month'));
        return $this->getTurnover($producer, $period, $format);
    }

    public function getYearsWithTurnover(Producer $producer): array
    {
        $year = date('Y');
        $yearsArray = [];

        for($i = 0; $i <= 10; $i++) {
            if($this->hasTurnoverOverTheYear($producer, $year - $i)) {
                array_unshift($yearsArray, $year - $i);
            }
        }

        return $yearsArray;
    }

    public function hasTurnoverOverTheYear(Producer $producer, int $year): bool
    {
        for($month = 1; $month <= 12; $month++) {
            if($this->getTurnover($producer, $year.'-'.$month)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Retourne le CA du producteur pour un mois donné
     */
    public function getTurnover(Producer $producer, string $period = '', bool $format = false)
    {
        if (!$period) {
            $period = date('Y-m');
        }

        $connection = \Yii::$app->getDb();
        $command = $connection->createCommand(
            '
            SELECT SUM(product_order.price * product_order.quantity) AS turnover
            FROM `order`, product_order, distribution
            WHERE `order`.id = product_order.id_order
            AND distribution.id_producer = :id_producer
            AND `order`.id_distribution = distribution.id
            AND distribution.date > :date_begin
            AND distribution.date < :date_end',
            [
                ':date_begin' => date('Y-m-31', strtotime("-1 month", strtotime($period))),
                ':date_end' => date('Y-m-01', strtotime("+1 month", strtotime($period))),
                ':id_producer' => $producer->id
            ]
        );

        $result = $command->queryOne();
        $turnover = $result['turnover'];

        if ($format) {
            return number_format($turnover, 2) . ' €';
        } else {
            return $turnover;
        }
    }

    public function getTurnoverByNumberMonths(Producer $producer, $numberMonths = 1)
    {
        $totalTurnover = 0;

        for ($i = 1; $i <= $numberMonths; $i++) {
            $timeMonth = strtotime('-' . $i . ' month');
            $month = date('Y-m', $timeMonth);
            $totalTurnover += $this->getTurnover($producer, $month);
        }

        return $totalTurnover;
    }

    public function getAmountToBeBilledByMonth(Producer $producer, $month, bool $format = false)
    {
        $turnover = $this->getTurnover($producer, $month);
        return $this->producerPriceRangeRepository->getAmountToBeBilledByTurnover($turnover, $format);
    }

    public function getDatasChartTurnoverStatistics(Producer $producer, int $year)
    {
        $interval = new \DateInterval('P1M');
        $start = new \DateTime($year.'-01-01');
        if($year == date('Y')) {
            $end = new \DateTime('last day of this month');
        }
        else {
            $end = new \DateTime($year.'-12-31');
        }
        $period = new \DatePeriod($start, $interval, $end);

        $data = [];
        $dataLabels = [];

        foreach ($period as $date) {
            $month = $date->format('m/Y');
            $dataLabels[] = $month;
            $turnover = $this->getTurnover($producer, $date->format('Y-m'));
            $data[$month] = $turnover;
        }

        // création d'un tableau sans index car chart.js n'accepte pas les index
        $dataNoIndex = [];
        foreach ($data as $key => $val) {
            $dataNoIndex[] = round($val, 2);
        }

        return [
            'labels' => $dataLabels,
            'data' => $dataNoIndex
        ];
    }

    public function getSummaryAmountsToBeBilled(Producer $producer, string $label, int $numberOfMonths = 1, $context = 'list'): string
    {
        $text = '';
        $numMonthCurrent = date('m');
        $sumInvoicePrice = 0;

        if ($numberOfMonths == 1
            || ($numberOfMonths == 3 && (in_array($numMonthCurrent, [1, 4, 7, 10])))
            || ($numberOfMonths == 6 && (in_array($numMonthCurrent, [1, 7])))
            || $context == 'billing') {

            for ($i = 1; $i <= $numberOfMonths; $i++) {
                $timeMonth = strtotime('-' . $i . ' month');
                $month = date('Y-m', $timeMonth);
                $turnover = $this->getTurnover($producer, $month);

                if ($turnover) {
                    if ($numberOfMonths > 1) {
                        $text .= ucfirst(strftime('%B ', $timeMonth)) . ' ' . date('Y', $timeMonth) . ' : ';
                    }
                    $isBold = $this->producerSolver->isBillingTypeClassic($producer) && !$producer->option_billing_permanent_transfer;
                    if ($isBold) $text .= '<strong>';
                    $text .= $this->producerPriceRangeRepository->getAmountToBeBilledByTurnover($turnover, true);
                    if ($isBold) $text .= '</strong>';
                    $text .= ' (' . Price::format($turnover, 0) . ' CA)';
                    $text .= '<br />';

                    $sumInvoicePrice += $this->producerPriceRangeRepository->getAmountToBeBilledByTurnover($turnover, false);
                }
            }

            if (strlen($text)) {
                $text = '<i>' . $label . '</i>' . ' : <br />' . $text;
            }
        }

        if ($numberOfMonths > 1 && $sumInvoicePrice > 0) {
            $text .= '<br />Total : <strong>' . Price::format($sumInvoicePrice, 0) . '</strong>';
        }

        return $text;
    }

    public function getAmountBilledLastMonth(Producer $producer): float
    {
        if ($this->producerSolver->isBillingTypeClassic($producer)) {
            $month = date('Y-m', strtotime('-1 month'));
            return $this->getAmountToBeBilledByMonth($producer, $month);
        } elseif ($this->producerSolver->isBillingTypeFreePrice($producer)) {
            return $producer->free_price;
        }

        return 0;
    }

    public function getOnlinePaymentMinimumAmount(): float
    {
        $onlinePaymentMinimumAmount = $this->producerSolver->getConfig('option_online_payment_minimum_amount');
        if (!$onlinePaymentMinimumAmount) {
            $onlinePaymentMinimumAmount = Producer::ONLINE_PAYMENT_MINIMUM_AMOUNT_DEFAULT;
        }

        return $onlinePaymentMinimumAmount;
    }

    public function getNameProducer(User $user): string
    {
        $producer = $this->findOneProducerById($user->id_producer);
        return $producer->name;
    }

    public function isDocumentDisplayOrders(DocumentInterface $document)
    {
        return ($this->documentSolver->getClass($document) == 'Invoice') ?
            $this->producerSolver->getConfig('document_display_orders_invoice') :
            $this->producerSolver->getConfig('document_display_orders_delivery_note');
    }

    /**
     * Retourne le mode de fonctionnement du crédit d'un point de vente.
     */
    public function getPointSaleCreditFunctioning(PointSale $pointSale): string
    {
        return strlen($pointSale->credit_functioning) > 0 ?
            $pointSale->credit_functioning :
            $this->producerSolver->getConfig('credit_functioning');
    }

    public function findProducersActive()
    {
        return $this->queryProducersActive()->find();
    }

    public function countProducersActiveWithTurnover(): int
    {
        $count = 0;
        $producersArray = $this->findProducersActive();

        foreach ($producersArray as $producer) {
            if ($this->getTurnoverByNumberMonths($producer, 3)) {
                $count++;
            }
        }

        return $count;
    }

    public function findProducers()
    {
        return $this->createQuery()->find();
    }

    public function findProducersWithTestimonials(): array
    {
        return $this->queryProducersActive()
            ->filterHasOptionTestimony()
            ->find();
    }

    public function findProducersWithTimeSaved(): array
    {
        return $this->queryProducersActive()
            ->filterHasOptionTimeSaved()
            ->find();
    }

    public function countProducersWithTimeSaved(): int
    {
        return count($this->findProducersWithTimeSaved());
    }

    public function getTimeSavedByProducersAverage(): int
    {
        $producersWithTimeSavedArray = $this->findProducersWithTimeSaved();
        $countProducersWithOptionTimeSaved = count($producersWithTimeSavedArray);

        if ($countProducersWithOptionTimeSaved) {
            $sumTimeSaved = 0;
            foreach ($producersWithTimeSavedArray as $producerWithTimeSaved) {
                $sumTimeSaved += $producerWithTimeSaved->option_time_saved;
            }

            return round($sumTimeSaved / $countProducersWithOptionTimeSaved);
        }

        return 0;
    }
}