<?php

namespace TdySports\Controller\Rest;

use TdyGames\Model\Contest;
use TdyGames\Model\Mixture;
use TdyGames\Model\Slot;
use TdySports\Model\Schedule;
use TdySports\Model\Standing;
use TdySports\Model\Week;
use TdySports\InputFilter\Week as WeekInputFilter;
use TdyCommons\Controller\BaseRestController;
use Zend\View\Model\JsonModel;

class WeeksController extends BaseRestController
{
    protected $identifierName = 'wk';

    /**
     * @return JsonModel
     */
    public function getList()
    {
        $this->getLogger()->debug('WeeksController::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->getWeeksTable()->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;
        }

        $weeks = iterator_to_array($resultSet, false);

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

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

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

        ]);
    }

    /**
     * @param int|string $wk
     *
     * @return JsonModel
     */
    public function get($wk)
    {
        $week = $this->getWeeksTable()->get($wk);
        $week->setServiceLocator($this->getServiceLocator());

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

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

        $data['id'] = 0;

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

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

            $data = $inputFilter->getValues();

            $week            = new Week($data);
            $week->createdBy = $this->getCurrentAdmin()->id;

            $week = $this->getWeeksTable()->setIndent(self::LOGGER_INDENT)->save($week);

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

            $this->getLogger()->info(self::LOGGER_INDENT . 'Week ' . $week->name . ' has been created.');
            $this->getLogger()->debug('WeeksController::create() end.');

            return $this->statusOk([
                'entry'      => $week->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('WeeksController::create() end.');

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

    /**
     * @param int|string $wk
     * @param mixed      $data
     *
     * @return JsonModel
     */
    public function update($wk, $data)
    {
        switch ($data['action']) {
            case 'endweek':
                $week = $this->getWeeksTable()->get($wk);
                $this->endTheWeek($week);

                return $this->statusOk([
                    'entry'      => $week->toStdClass(),
                    'pagination' => [
                        'pageNumber' => 1,
                        'pageSize'   => 1,
                        'totalItems' => 1,
                        'totalPages' => 1,
                    ],
                ]);
                break;
            case 'update':
            default:
                $this->getLogger()->debug('WeeksController::update() start.');

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

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

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

                    $week            = new Week($data);
                    $week->updatedBy = $this->getCurrentAdmin()->id;
                    $week            = $this->getWeeksTable()->setIndent(self::LOGGER_INDENT)->save($week);

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

                    $this->getLogger()->info(self::LOGGER_INDENT . 'Week ' . $week->name . ' has been updated.');
                    $this->getLogger()->debug('WeeksController::update() end.');

                    return $this->statusOk([
                        'entry'      => $week->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('WeeksController::update() end.');

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

    }

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

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

        if ($wk > 0) {
            $week = $this->getWeeksTable()->get($wk);
            if ($week) {

                $this->getLogger()->debug(self::LOGGER_INDENT . 'Week ID  : ' . $wk);
                $this->getLogger()->debug(self::LOGGER_INDENT . 'Week Name: ' . $week->name);

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

                $this->getLogger()->info(self::LOGGER_INDENT . 'Week ' . $week->name . ' has been deleted.');
                $this->getLogger()->debug('WeeksController::delete() end.');

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

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

            return $this->statusNotFound();
        }
    }

    protected function endTheWeek(Week $week)
    {
        $this->getLogger()->debug('WeeksController::endTheWeek() start.');
        $connection = $this->getWeeksTable()->getTableGateway()->getAdapter()->getDriver()->getConnection();

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

        try {
            $f = ['week_id' => $week->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 = [];

            $this->getLogger()->debug(self::LOGGER_INDENT . 'Record standings.');

            /** @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
                ];

                $difference = $schedule->scoreHome - $schedule->scoreAway;

                $standingHome = $this->getStandingsTable()->getByParams([
                    'season_id' => $schedule->seasonId,
                    'league_id' => $schedule->leagueId,
                    'team_id' => $schedule->teamHome,
                ]);

                $standingAway = $this->getStandingsTable()->getByParams([
                    'season_id' => $schedule->seasonId,
                    'league_id' => $schedule->leagueId,
                    'team_id' => $schedule->teamAway,
                ]);

                if ($difference > 0) {
                    $standingHome->wins++;
                    $standingAway->losses++;
                } else if ($difference < 0) {
                    $standingHome->losses++;
                    $standingAway->wins++;
                } else {
                    $standingHome->ties++;
                    $standingAway->ties++;
                }

                $standingHome->calculatePercentage();
                $standingAway->calculatePercentage();

                $this->getStandingsTable()->save($standingHome);
                $this->getStandingsTable()->save($standingAway);
            }


            $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.

                            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) {

                            // Court Location Rules.

                            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;
                            }

                            // Divisional Match-Up Rules

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

                            // Q&A Rules. Will define later
                        }

                        $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('WeeksController::endTheWeek() 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('WeeksController::endTheWeek() end.');
        }


    }

}
