<?php

namespace App\Utils;

use App\Entity\AccountComptoir;
use App\Entity\AccountGroupe;
use App\Entity\Adherent;
use App\Entity\Flux;
use App\Entity\Groupe;
use App\Entity\Prestataire;
use App\Enum\ChartEnum;
use App\Enum\CurrencyEnum;
use App\Enum\MoyenEnum;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\UX\Chartjs\Builder\ChartBuilderInterface;
use Symfony\UX\Chartjs\Model\Chart;

/**
 * ChartUtils : Utils for stats and charts on admin dashboard
 */
class ChartUtils
{
    private $em;
    private $session;
    private $security;
    private $chartBuilder;

    public function __construct(SessionInterface $session, CustomEntityManager $em, Security $security, ChartBuilderInterface $chartBuilder)
    {
        $this->em = $em;
        $this->session = $session;
        $this->security = $security;
        $this->chartBuilder = $chartBuilder;
    }

    /**
     * Get Chart given the type and the params
     * @param  string $type   Type of chart, one of ChartEnum
     * @param  array  $params array of params depending on the chart you want
     * @return ?Chart                    return the Chart object with datas and labels
     */
    public function getMlcChart(string $type, array $params = []): ?Chart
    { 
        switch ($type) {
            case 'fluxbytype':
                return $this->getChartForStatsType($params);//['type' => $type, 'byTypes' => $byTypes, 'byVolume' => $byVolume, 'options' => $options]);
            case 'groupeMlc':
                return $this->getChartForBalancesGroupe();
            case 'comptoirMlc':
                return $this->getChartForBalancesComptoir();
            case 'nbuser':
                // check params
                $grouByType = isset($params['grouByType']) ? $params['grouByType'] : 'month';
                $withadherent = isset($params['withadherent']) ? $params['withadherent'] : true;
                $withpresta = isset($params['withpresta']) ? $params['withpresta'] : true;
                
                return $this->getChartForStats($grouByType, $withpresta, $withadherent);
            case 'nbpresta':
                return $this->getChartForStats($grouByType, true, false);
            case 'nbadherent':
                return $this->getChartForStats(false, true);
            case 'nombretransac':
                return $this->getChartForStatsMouvements();
            case 'volumetransac':
                return $this->getChartForStatsMouvements(false);
            default:
                return null;
        }
    }

    /**
     * Get chart for flux by specific Flux::TYPE_*** by months
     * 
     * @param  array|null   $byFluxType Flux::TYPE_*** or null for all
     * @param  bool|boolean $byVolume   if true, show volume, if false show amount
     * @return ?Chart                    return the Chart object with datas and labels
     */
    public function getChartForStatsType(?array $params): ?Chart
    {
        // check params
        $type = isset($params['type']) ? $params['type'] : Chart::TYPE_LINE;
        $byFluxType = isset($params['byTypes']) ? $params['byTypes'] : [];
        $byVolume = isset($params['byVolume']) ? $params['byVolume'] : true;
        $options = isset($params['options']) ? $params['options'] : [];
        $groupBy = isset($params['groupBy'])?$params['groupBy']:'month';
        
        if ($byVolume) {
            $functionRepository = 'findCountByParams';
        } else {
            $functionRepository = 'findSumByParams';
        }
        $datasArray = [];
        $mL = [];
        $colors = ['red', 'orange', 'yellow', 'green', 'blue', 'violet', 'grey', 'pink'];
        $colorCount = 0;

        if (!empty($byFluxType)) {
            // Chart of certain type of flux with same options array
            if ('all' === $byFluxType) {
                $byFluxType = [
                    Flux::TYPE_ACHAT,
                    Flux::TYPE_COTISATION,
                    Flux::TYPE_RECONVERSION,
                    Flux::TYPE_RETRAIT,
                    Flux::TYPE_TRANSACTION,
                    Flux::TYPE_TRANSFERT,
                    Flux::TYPE_VENTE,
                    Flux::TYPE_VENTE_EMLC,
                ];
            }
            
            foreach($byFluxType as $type) {
                $options = array_unique(array_merge($options, ['parenttype' => $type]));
                $datas = $this->em->getRepository(Flux::class)->$functionRepository($options);
                list($monthsLabelNew, $dataSet) = $this->getDatasetAndLabelsFromData($datas, $groupBy);
                $mL = array_unique(array_merge($monthsLabelNew, $mL));
                list($bgColor, $bdColor) = $this->getColorsFromText($colors[$colorCount]);
                $colorCount++;
                $colorCount = $colorCount>7?0:$colorCount;
                $datasArray[] = [
                    'label' => ucwords(str_replace('_', ' ', $type)),
                    'data' => $dataSet,
                    'bgColor' => $bgColor,
                    'bdColor' => $bdColor
                ];
            }
        } elseif (!empty($options)) {
            // Charts for specific options
            foreach($options as $option) {
                $byVolume = isset($option['byVolume']) ? $option['byVolume'] : true;
                if ($byVolume) {
                    $functionRepository = 'findCountByParams';
                } else {
                    $functionRepository = 'findSumByParams';
                }
                $datas = $this->em->getRepository(Flux::class)->$functionRepository($option['filter']);
                list($monthsLabelNew, $dataSet) = $this->getDatasetAndLabelsFromData($datas, $groupBy);
                $mL = array_unique(array_merge($monthsLabelNew, $mL));
                list($bgColor, $bdColor) = $this->getColorsFromText(isset($option['color'])?$option['color']:$colors[$colorCount], isset($option['opacity'])?$option['opacity']:1, isset($option['samecolor'])?$option['samecolor']:true);
                $colorCount++;
                $colorCount = $colorCount>7?0:$colorCount;
                $datasArray[] = [
                    'label' => isset($option['label'])?$option['label']:'',
                    'data' => $dataSet,
                    'bgColor' => $bgColor,
                    'bdColor' => $bdColor
                ];
            }
        } else {
            return null;
        }

        if ($type == Chart::TYPE_LINE || $type == Chart::TYPE_BAR) {
            $chart = $this->getMyChartForStats($mL, $datasArray, $type);
        } else {
            $chart = $this->getMyChartForStats($mL, $datasArray, Chart::TYPE_LINE);
        }

        return $chart;
    }
    
    /**
     * Get chart for all movement (transfert, transaction, CB and reconversion)
     * @param  boolean $byVolume if true, show volume, if false show amount
     * @return ?Chart             return the Chart object with datas and labels
     */
    public function getChartForStatsMouvements($byVolume = true): ?Chart
    {
        if ($byVolume) {
            $functionRepository = 'findSumByParams';
        } else {
            $functionRepository = 'findCountByParams';
        }
        $datas = $this->em->getRepository(Flux::class)->$functionRepository(['parenttype' => Flux::TYPE_TRANSFERT]);
        list($monthsLabel, $dataSet) = $this->getDatasetAndLabelsFromData($datas);
        list($bgColor, $bdColor) = $this->getColorsFromText('green');

        $datasTransac = $this->em->getRepository(Flux::class)->$functionRepository(['parenttype' => Flux::TYPE_TRANSACTION]);
        list($monthsLabelTransac, $dataSetTransac) = $this->getDatasetAndLabelsFromData($datasTransac);
        list($bgColorTransac, $bdColorTransac) = $this->getColorsFromText('orange');
        
        $datasCB = $this->em->getRepository(Flux::class)->$functionRepository(['moyen' => MoyenEnum::MOYEN_CB]);
        list($monthsLabelCB, $dataSetCB) = $this->getDatasetAndLabelsFromData($datasCB);
        list($bgColorCB, $bdColorCB) = $this->getColorsFromText('violet');
        
        $dataReconv = $this->em->getRepository(Flux::class)->$functionRepository(['parenttype' => Flux::TYPE_RECONVERSION]);
        list($monthsLabelReconv, $dataSetReconv) = $this->getDatasetAndLabelsFromData($dataReconv);
        list($bgColorReconv, $bdColorReconv) = $this->getColorsFromText('red');
        
        $mL = array_unique(array_merge($monthsLabelTransac, $monthsLabel, $monthsLabelCB, $monthsLabelReconv));
        
        $chart = $this->getMyChartForStats($mL, [
            [
                'label' => 'Virements',
                'data' => $dataSet,
                'bgColor' => $bgColor,
                'bdColor' => $bdColor
            ],
            [
                'label' => 'Transactions',
                'data' => $dataSetTransac,
                'bgColor' => $bgColorTransac,
                'bdColor' => $bdColorTransac
            ],
            [
                'label' => 'Carte Bancaire',
                'data' => $dataSetCB,
                'bgColor' => $bgColorCB,
                'bdColor' => $bdColorCB
            ],
            [
                'label' => 'Reconversion',
                'data' => $dataSetReconv,
                'bgColor' => $bgColorReconv,
                'bdColor' => $bdColorReconv
            ],
        ]);

        return $chart;
    }

    /**
     * Get chart for balance of all groupes
     * @return ?Chart     return the Chart object with datas and labels
     */
    public function getChartForBalancesGroupe(): ?Chart
    {
        $datas = $this->em->getRepository(AccountGroupe::class)->findAllBalancesByName(CurrencyEnum::CURRENCY_MLC);
        $datasNew = [];
        $labelsNew = [];
        $colorStartBlue = 255;
        foreach ($datas as $data) {
            if ($data['balance'] > 0) {
                $labelsNew[] = $data['name'];
                $datasNew[] = [
                    'label' => $data['name'],
                    'data' => $data['balance'],
                    'bgColor' => 'rgba(54, 162, '.$colorStartBlue.', 0.2)',
                    'bdColor' => 'rgba(54, 162, '.$colorStartBlue.')',
                ];
                $colorStartBlue = $colorStartBlue-30;
            }
        }
        return $this->getMyChartForStats($labelsNew, $datasNew, Chart::TYPE_PIE);
    }

    /**
     * Get chart for balance of all "comptoir" (restricted if you are a groupe manager to your "comptoir")
     * @return ?Chart     return the Chart object with datas and labels
     */
    public function getChartForBalancesComptoir(): ?Chart
    {
        if (!empty($this->session->get('_groupegere'))) {
            $datas = $this->em->getRepository(AccountComptoir::class)->findAllBalancesByName(CurrencyEnum::CURRENCY_MLC, $this->session->get('_groupegere'));
        } else {
            $datas = $this->em->getRepository(AccountComptoir::class)->findAllBalancesByName(CurrencyEnum::CURRENCY_MLC);
        }
        $datasNew = [];
        $labelsNew = [];
        $colorStartBlue = 255;
        foreach ($datas as $data) {
            if ($data['balance'] > 0) {
                $labelsNew[] = $data['name'];
                $datasNew[] = [
                    'label' => $data['name'],
                    'data' => $data['balance'],
                    'bgColor' => 'rgba(124, 122, '.$colorStartBlue.', 0.2)',
                    'bdColor' => 'rgba(124, 122, '.$colorStartBlue.')',
                ];
                $colorStartBlue = $colorStartBlue-15;
            }
        }
        return $this->getMyChartForStats($labelsNew, $datasNew, Chart::TYPE_PIE);
    }

    /**
     * Get chart for stats for number of new adherent and/or new prestataire by months
     * @param  string       $groupByType By 'year', 'month', 'day'
     * @param  bool|boolean $withPresta With prestataires
     * @param  bool|boolean $withAdh    With adherents
     * @return ?Chart             return the Chart object with datas and labels
     */
    public function getChartForStats(?string $groupByType = 'month', ?bool $withPresta = true, ?bool $withAdh = true): ?Chart
    {
        $groupe = null;
        $datasArray = [];
        $mL = [];
        // if (!empty($this->session->get('_groupegere'))) {
        //     $groupe = $this->session->get('_groupegere');
        //     $groupe = $this->em->getRepository(Groupe::class)->findOneById($groupe->getId());
        // }
        if ($withPresta) {
            $datas = $this->em->getRepository(Prestataire::class)->findCountBy($groupByType, $groupe);
            list($monthsLabel, $dataSet) = $this->getDatasetAndLabelsFromData($datas, $groupByType);
            list($bgColor, $bdColor) = $this->getColorsFromText('red');
            $mL = $monthsLabel;
            $datasArray[] = [
                'label' => 'Prestataires',
                'data' => $dataSet,
                'bgColor' => $bgColor,
                'bdColor' => $bdColor
            ];
        }

        if ($withAdh) {
            $datasAdh = $this->em->getRepository(Adherent::class)->findCountBy($groupByType, $groupe);
            list($monthsLabelAdh, $dataSetAdh) = $this->getDatasetAndLabelsFromData($datasAdh, $groupByType);
            list($bgColorAdh, $bdColorAdh) = $this->getColorsFromText('blue');
            if (!$withPresta) {
                $mL = $monthsLabelAdh;
            } else {
                $mL = array_unique(array_merge($monthsLabel, $monthsLabelAdh));
            }
            $datasArray[] = [
                'label' => 'Adhérents',
                'data' => $dataSetAdh,
                'bgColor' => $bgColorAdh,
                'bdColor' => $bdColorAdh
            ];
        }

        return $this->getMyChartForStats($mL, $datasArray);
    }

    /**
     * Get dataset and labels for chart by months
     * @param  array        $datas        array of datas
     * @param  string       $groupByType  'year' 'month' or 'day'
     * @param  bool|boolean $withAddition with addition of value or no, default false
     * @return array       (label, dataset)
     */
    private function getDatasetAndLabelsFromData(array $datas, string $groupByType = 'month', bool $withAddition = false): array
    {
        $monthsLabel = array_values(array_column($datas, $groupByType));
        if ($groupByType == 'month') {
            $monthsLabel = array_map(function($v){ 
                $monthFr = ['Jan', 'Fév', 'Mars', 'Avr', 'Mai', 'Juin', 'Juil', 'Août', 'Sep', 'Oct', 'Nov', 'Déc'];
                
                return $monthFr[substr($v, 5, strlen($v)-5)-1] . ' ' . substr($v, 0, 4);
            }
            , $monthsLabel);
        }
        $months = array_column($datas, 'total');
        $dataSet = [];
        if ($withAddition) {
            $currentValue = 0;
            foreach ($months as $month) {
                $currentValue += $month;
                $dataSet[] = $currentValue;
            }
        } else {
            $dataSet = array_values($months);
        }

        return [$monthsLabel, $dataSet];
    }

    /**
     * Get colors from text for chart
     * @param  string  $txt Can be 'red', 'orange', 'yellow', 'green', 'blue', 'violet', 'grey'
     * @param  float   $opacity Opacity of the color
     * @param  boolean $sameColor If true, same color apply for background and border, if false, background opacity is 0.2
     * 
     * @return array [color background, color border]
     */
    private function getColorsFromText(string $txt, float $opacity = 1, $sameColor = true): array
    {
        switch($txt) {
            case 'red':
                return ['rgba(255, 99, 132, '.($sameColor?$opacity:'0.2').')', 'rgba(255, 99, 132, '.$opacity.')'];
            case 'orange':
                return ['rgba(255, 159, 64, '.($sameColor?$opacity:'0.2').')', 'rgba(255, 159, 64, '.$opacity.')'];
            case 'yellow':
                return ['rgba(255, 205, 86, '.($sameColor?$opacity:'0.2').')', 'rgba(255, 205, 86, '.$opacity.')'];
            case 'green':
                return ['rgba(75, 192, 192, '.($sameColor?$opacity:'0.2').')', 'rgba(75, 192, 192, '.$opacity.')'];
            case 'blue':
                return ['rgba(54, 162, 235, '.($sameColor?$opacity:'0.2').')', 'rgba(54, 162, 235, '.$opacity.')'];
            case 'violet':
                return ['rgba(153, 102, 255, '.($sameColor?$opacity:'0.2').')', 'rgba(153, 102, 255, '.$opacity.')'];
            case 'pink':
                return ['rgba(232, 62, 140, '.($sameColor?$opacity:'0.2').')', 'rgba(232, 62, 140, '.$opacity.')'];
            case 'grey':
            default:
                return ['rgba(201, 203, 207, '.($sameColor?$opacity:'0.2').')', 'rgba(201, 203, 207, '.$opacity.')'];
        }
    }

    /**
     * @param  array    labels 
     * @param  array    datas
     * @param  string   Type of chart from Chart::TYPE_***
     * @return ?Chart   Chart object
     */
    private function getMyChartForStats(array $labels, array $datas, string $type = Chart::TYPE_BAR): ?Chart
    {
        $datasets = [];
        if ($type == Chart::TYPE_PIE) {
            $datasets = [[
                'label' => '',
                'data' => array_map(function($val) {
                    return $val['data'];
                }, $datas),
                'backgroundColor' => array_map(function($val) {
                    return $val['bgColor'];
                }, $datas),
                'borderColor' => array_map(function($val) {
                    return $val['bdColor'];
                }, $datas),
                'hoverOffset' => 4
            ]];
        }
        foreach ($datas as $data) {
            if ($type == Chart::TYPE_BAR) {
                $datasets[] = [
                    'label' => isset($data['label'])?$data['label']:'',
                    'barPercentage' => 0.5,
                    'barThickness' => 6,
                    'maxBarThickness' => 8,
                    'minBarLength' => 2,
                    'data' => isset($data['data'])?$data['data']:[],
                    'backgroundColor' => [
                        isset($data['bgColor'])?$data['bgColor']:'rgba(201, 203, 207, 0.2)',
                    ],
                    'borderColor' => [
                        isset($data['bdColor'])?$data['bdColor']:'rgb(201, 203, 207)'
                    ],
                    'borderWidth' => 1
                ];
            } else if ($type == Chart::TYPE_LINE) {
                $datasets[] = [
                    'label' => isset($data['label'])?$data['label']:'',
                    'data' => isset($data['data'])?$data['data']:[],
                    'backgroundColor' => [
                        isset($data['bgColor'])?$data['bgColor']:'rgba(201, 203, 207, 0.2)',
                    ],
                    'borderColor' => [
                        isset($data['bdColor'])?$data['bdColor']:'rgb(201, 203, 207)'
                    ],
                    'fill' => false,
                    'tension' => 0.4,
                ];
            }
        }
        $chart = $this->chartBuilder->createChart($type);
        $chart->setData([
            'labels' => array_values($labels),
            'datasets' => $datasets,
        ]);
        if ($type == Chart::TYPE_PIE) {
            $chart->setOptions([
                'aspectRatio' => 1.5,
            ]);
        } else if ($type == Chart::TYPE_BAR) {
            $chart->setOptions([
                // 'scales' => [
                //     'x' => [
                //         ['grid' => ['lineWidth' => 2, 'offset' => true]],
                //     ],
                // ],
                'aspectRatio' => 1.5,
            ]);
        } else if ($type == Chart::TYPE_LINE) {
            $chart->setOptions([
                'aspectRatio' => 1.5,
            ]);
        }

        return $chart;
    }
}