<?php

namespace TdySports\Controller\Rest;

use TdyGames\Model\Contest;
use TdyGames\Model\Mixture;
use TdyGames\Model\Slot;
use TdySports\Model\Schedule;
use TdySports\Model\Season;
use TdySports\InputFilter\Season as SeasonInputFilter;
use TdyCommons\Controller\BaseRestController;
use TdySports\Model\Standing;
use TdySports\Model\Team;
use Zend\View\Model\JsonModel;

class SeasonsController extends BaseRestController
{
    protected $identifierName = 'sn';

    /**
     * @return JsonModel
     */
    public function getList()
    {
        $this->getLogger()->debug('SeasonsController::getList() start.');

        $q = $this->params()->fromQuery('q', '');
        $p = (int) $this->params()->fromQuery('p', 1);
        $l = (int) $this->params()->fromQuery(
            'l', $this->getSettingsTable()->get('items-per-page')
        );
        $s = $this->params()->fromQuery('s', []);
        $f = $this->params()->fromQuery('f', []);
        $x = [];

        $resultSet = $this->getSeasonsTable()->setIndent(self::LOGGER_INDENT)->fetch($q, $x, $f, $s, true);
        $resultSet->setCurrentPageNumber((int) $p);
        $resultSet->setItemCountPerPage($l);

        if ($p > 0) {
            $i = (($p - 1) * $l);
        } else {
            $i = 0;
        }

        $seasons = iterator_to_array($resultSet, false);

        /**
         * @var int    $k
         * @var Season $season
         */
        foreach ($seasons as $k => $season) {
            $season->setServiceLocator($this->getServiceLocator());
            $seasons[$k]->row = ++$i;
            $seasons[$k]      = $season->toStdClass();
        }

        $this->getLogger()->debug('SeasonsController::getList() end.');

        return $this->statusOk(
            [
                'entries'    => $seasons,
                'pagination' => [
                    'pageNumber' => $p,
                    'pageSize'   => $l,
                    'totalItems' => $resultSet->getTotalItemCount(),
                    'totalPages' => $resultSet->count(),
                ],

            ]
        );
    }

    /**
     * @param int|string $sn
     *
     * @return JsonModel
     */
    public function get($sn)
    {
        $season = $this->getSeasonsTable()->get($sn);
        $season->setServiceLocator($this->getServiceLocator());

        return $this->statusOk(
            [
                'entry'      => $season->toStdClass(),
                'pagination' => [
                    'pageNumber' => 1,
                    'pageSize'   => 1,
                    'totalItems' => 1,
                    'totalPages' => 1,
                ],
            ]
        );
    }

    /**
     * @param mixed $data
     *
     * @return JsonModel
     */
    public function create($data)
    {
        $this->getLogger()->debug('SeasonsController::create() start.');

        $data['id'] = 0;

        $inputFilter = new SeasonInputFilter($this->getServiceLocator());
        $inputFilter->setData($data);

        if ($inputFilter->isValid()) {
            $this->getLogger()->debug(self::LOGGER_INDENT . 'Input Filter is valid. Continue.');

            $data = $inputFilter->getValues();

            $season            = new Season($data);
            $season->createdBy = $this->getCurrentAdmin()->id;

            $season = $this->getSeasonsTable()->setIndent(self::LOGGER_INDENT)->save($season);

            $season->setServiceLocator($this->getServiceLocator());

            $this->getLogger()->info(self::LOGGER_INDENT . 'Season ' . $season->name . ' has been created.');
            $this->getLogger()->debug('SeasonsController::create() end.');

            return $this->statusOk(
                [
                    'entry'      => $season->toStdClass(),
                    'pagination' => [
                        'pageNumber' => 1,
                        'pageSize'   => 1,
                        'totalItems' => 1,
                        'totalPages' => 1,
                    ],
                ]
            );
        } else {
            $this->getLogger()->debug(self::LOGGER_INDENT . 'Input Filter is invalid. Bugging out.');
            $this->getLogger()->debug('SeasonsController::create() end.');

            return $this->statusBadRequest(
                [
                    'system'    => [
                        'id'          => 4001,
                        'description' => 'Invalid Request',
                    ],
                    'companies' => [
                        'description' => 'Submission is invalid.',
                        'details'     => $inputFilter->getMessages(),
                    ],
                ]
            );
        }
    }

    /**
     * @param int|string $sn
     * @param mixed      $data
     *
     * @return JsonModel
     */
    public function update($sn, $data)
    {
        $this->getLogger()->debug('SeasonsController::update() start.');

        switch ($data['action']) {
            case 'endseason':
                $season = $this->getSeasonsTable()->get($sn);
                $this->endTheSeason($season);

                return $this->statusOk(
                    [
                        'entry'      => $season->toStdClass(),
                        'pagination' => [
                            'pageNumber' => 1,
                            'pageSize'   => 1,
                            'totalItems' => 1,
                            'totalPages' => 1,
                        ],
                    ]
                );
                break;
            case 'generatestandings':
                $this->getLogger()->debug(self::LOGGER_INDENT . 'Selected Generate Standings option.');

                $season = $this->getSeasonsTable()->get($sn);
                $this->generateStandings($season);

                return $this->statusOk(
                    [
                        'entry'      => [
                            'result' => true,
                        ],
                        'pagination' => [
                            'pageNumber' => 1,
                            'pageSize'   => 1,
                            'totalItems' => 1,
                            'totalPages' => 1,
                        ],
                    ]
                );
                break;
            case 'update':
            default:
                $this->getLogger()->debug(self::LOGGER_INDENT . 'Selected Update option.');

                $data['id'] = (int) $sn;

                $inputFilter = new SeasonInputFilter($this->getServiceLocator(), true);
                $inputFilter->setData($data);

                if ($inputFilter->isValid()) {
                    $this->getLogger()->debug(self::LOGGER_INDENT . 'Input Filter is valid. Continue.');

                    $season            = new Season($data);
                    $season->updatedBy = $this->getCurrentAdmin()->id;
                    $season            = $this->getSeasonsTable()->setIndent(self::LOGGER_INDENT)->save($season);

                    $season->setServiceLocator($this->getServiceLocator());

                    $this->getLogger()->info(self::LOGGER_INDENT . 'Season ' . $season->name . ' has been updated.');
                    $this->getLogger()->debug('SeasonsController::update() end.');

                    return $this->statusOk(
                        [
                            'entry'      => $season->toStdClass(),
                            'pagination' => [
                                'pageNumber' => 1,
                                'pageSize'   => 1,
                                'totalItems' => 1,
                                'totalPages' => 1,
                            ],
                        ]
                    );
                } else {
                    $this->getLogger()->debug(self::LOGGER_INDENT . 'Input Filter is invalid. Will not update.');
                    $this->getLogger()->debug('SeasonsController::update() end.');

                    return $this->statusBadRequest(
                        [
                            'system'    => [
                                'id'          => 4001,
                                'description' => 'Invalid Request',
                            ],
                            'companies' => [
                                'description' => 'Submission is invalid.',
                                'details'     => $inputFilter->getMessages(),
                            ],
                        ]
                    );
                }
                break;
        }

    }

    /**
     * @param int|string $sn
     *
     * @return JsonModel
     * @throws \Exception
     */
    public function delete($sn)
    {
        $this->getLogger()->debug('SeasonsController::delete() start.');

        $this->getLogger()->debug(self::LOGGER_INDENT . 'Getting info.');

        if ($sn > 0) {
            $season = $this->getSeasonsTable()->get($sn);
            if ($season) {

                $this->getLogger()->debug(self::LOGGER_INDENT . 'Season ID  : ' . $sn);
                $this->getLogger()->debug(self::LOGGER_INDENT . 'Season Name: ' . $season->name);

                $season->deletedBy = $this->getCurrentAdmin()->id;
                $this->getSeasonsTable()->setIndent(self::LOGGER_INDENT)->delete($season);

                $this->getLogger()->info(self::LOGGER_INDENT . 'Season ' . $season->name . ' has been deleted.');
                $this->getLogger()->debug('SeasonsController::delete() end.');

                return $this->statusOk(
                    [
                        'entry'      => null,
                        'pagination' => [
                            'pageNumber' => 1,
                            'pageSize'   => 1,
                            'totalItems' => 1,
                            'totalPages' => 1,
                        ],
                    ]
                );
            } else {
                $this->getLogger()->debug(self::LOGGER_INDENT . 'Season ID ' . $sn . ' does not exists.');
                $this->getLogger()->debug('SeasonsController::delete() end.');

                return $this->statusNotFound();
            }
        } else {
            $this->getLogger()->debug(self::LOGGER_INDENT . 'Season ID is requred for deletion.');
            $this->getLogger()->debug('SeasonsController::delete() end.');

            return $this->statusNotFound();
        }
    }

    protected function endTheSeason(Season $season)
    {
        $this->getLogger()->debug('SeasonsController::endTheSeason() start.');
        $connection = $this->getSeasonsTable()->getTableGateway()->getAdapter()->getDriver()->getConnection();

        $this->getLogger()->debug(self::LOGGER_INDENT . 'Transaction starts.');
        $connection->beginTransaction();

        try {
            $f = ['season_id' => $season->id];

            /**
             * @var array $schedules
             * @var array $contests
             */
            $schedules = iterator_to_array($this->getSchedulesTable()->fetch('', [], $f, [], false));
            $contests  = iterator_to_array($this->getContestsTable()->fetch('', [], $f, [], false));

            $scores = [];

            /** @var Schedule $schedule */
            foreach ($schedules as $schedule) {
                $scores[$schedule->teamHome] = [
                    'side'  => 'home',
                    'score' => $schedule->scoreHome,
                    'group' => $this->getTeamsTable()->get($schedule->teamHome)->groupId,
                ];
                $scores[$schedule->teamAway] = [
                    'side'  => 'away',
                    'score' => $schedule->scoreAway,
                    'group' => $this->getTeamsTable()->get($schedule->teamAway)->groupId,
                ];
            }

            $this->getLogger()->debug(self::LOGGER_INDENT . 'Save scores and calculate.');

            /** @var Contest $contest */
            foreach ($contests as $contest) {
                $slots = iterator_to_array($this->getContestsSlotsTable()->setIndent(self::LOGGER_INDENT)->fetch($contest->id));

                /** @var Slot $slot */
                foreach ($slots as $slot) {
                    $slot->totalPoints = 0;
                    $mixtures          = iterator_to_array($this->getContestsMixturesTable()->setIndent(self::LOGGER_INDENT . self::LOGGER_INDENT)->fetch(null, null, $slot->id));

                    /** @var Mixture $mixture */
                    foreach ($mixtures as $mixture) {
                        $mixture->setServiceLocator($this->getServiceLocator());

                        $mixture->scoreHome = $scores[$mixture->teamHome]['score'];
                        $mixture->scoreAway = $scores[$mixture->teamAway]['score'];

                        if ($mixture->bet == $mixture->teamHome) {
                            $points = $mixture->scoreHome - $mixture->scoreAway;
                        } else {
                            $points = $mixture->scoreAway - $mixture->scoreHome;
                        }

                        $win  = $points > 0;
                        $loss = $points < 0;
                        $draw = $points == 0;

                        if ($win) {

                            // Court Location Rules. Not yet implemented.

                            if ($scores[$mixture->teamHome]['side'] == 'home' && $scores[$mixture->teamAway]['side'] == 'away') {
                                $points += 1;
                            }
                            if ($scores[$mixture->teamHome]['side'] == 'away' && $scores[$mixture->teamAway]['side'] == 'home') {
                                $points += 1;
                            }
                            if ($scores[$mixture->teamHome]['side'] == 'home' && $scores[$mixture->teamAway]['side'] == 'home') {
                                $points += 1;
                            }
                            if ($scores[$mixture->teamHome]['side'] == 'away' && $scores[$mixture->teamAway]['side'] == 'away') {
                                $points += 1;
                            }

                            // Divisional Match-Up Rules

                            if ($mixture->getHomeTeam()->groupId == $mixture->getAwayTeam()->groupId) {
                                $points += 2;
                            }

                            // Q&A Rules. Will define later
                        }

                        if ($loss) {
                            if ($scores[$mixture->teamHome]['side'] == 'home' && $scores[$mixture->teamAway]['side'] == 'away') {
                                $points += 0;
                            }
                            if ($scores[$mixture->teamHome]['side'] == 'away' && $scores[$mixture->teamAway]['side'] == 'home') {
                                $points += 0;
                            }
                            if ($scores[$mixture->teamHome]['side'] == 'home' && $scores[$mixture->teamAway]['side'] == 'home') {
                                $points += 0;
                            }
                            if ($scores[$mixture->teamHome]['side'] == 'away' && $scores[$mixture->teamAway]['side'] == 'away') {
                                $points += 0;
                            }
                        }

                        $mixture->points = $points;
                        $slot->totalPoints += $points;

                        $this->getContestsMixturesTable()->save($mixture);
                    }


                    $this->getContestsSlotsTable()->save($slot);
                }


                // Sort the points, descending order and indicate the place.

                $slots = iterator_to_array($this->getContestsSlotsTable()->fetch($contest->id, '', [], [], ['total_points' => 'DESC'], false));

                $i = 0;

                /** @var Slot $slot */
                foreach ($slots as $slot) {
                    $slot->place = ++$i;
                    $this->getContestsSlotsTable()->setPlace($slot);
                }

            }

            $this->getLogger()->debug(self::LOGGER_INDENT . 'Committing...');
            $connection->commit();
            $this->getLogger()->debug(self::LOGGER_INDENT . 'Committed.');

            $this->getLogger()->debug('SeasonsController::endTheSeason() end.');
        } catch (\Exception $e) {
            $this->getLogger()->debug(self::LOGGER_INDENT . 'Tossed an Exception grenade: ' . $e->getMessage());

            $this->getLogger()->debug(self::LOGGER_INDENT . 'Rolling back...');
            $connection->rollback();
            $this->getLogger()->debug(self::LOGGER_INDENT . 'Rolled back.');

            $this->getLogger()->debug('SeasonsController::endTheSeason() end.');
        }


    }

    /**
     * @param Season $season
     */
    protected function generateStandings($season)
    {
        $this->getLogger()->debug('SeasonsController::generateStandings() start.');
        $connection = $this->getSeasonsTable()->getTableGateway()->getAdapter()->getDriver()->getConnection();

        $this->getLogger()->debug(self::LOGGER_INDENT . 'Transaction starts.');
        $connection->beginTransaction();

        try {
            $f = ['league_id' => $season->leagueId];

            $teamsRS = $this->getTeamsTable()->fetch('', [], $f);
            $teams   = iterator_to_array($teamsRS);


            /** @var Team $team */
            foreach ($teams as $team) {
                $standing           = new Standing();
                $standing->leagueId = $season->leagueId;
                $standing->seasonId = $season->id;
                $standing->teamId   = $team->id;

                $this->getStandingsTable()->save($standing);
            }

            $this->getLogger()->debug(self::LOGGER_INDENT . 'Committing...');
            $connection->commit();
            $this->getLogger()->debug(self::LOGGER_INDENT . 'Committed.');

            $this->getLogger()->debug('SeasonsController::generateStandings() end.');
        } catch (\Exception $e) {
            $this->getLogger()->debug(self::LOGGER_INDENT . 'Tossed an Exception grenade: ' . $e->getMessage());

            $this->getLogger()->debug(self::LOGGER_INDENT . 'Rolling back...');
            $connection->rollback();
            $this->getLogger()->debug(self::LOGGER_INDENT . 'Rolled back.');

            $this->getLogger()->debug('SeasonsController::generateStandings() end.');
        }
    }

}
