<?php

namespace TdySports\Controller\Rest;

use TdyGames\Model\Contest;
use TdyGames\Model\Mixture;
use TdyGames\Model\Slot;
use TdySports\Model\Schedule;
use TdySports\InputFilter\Schedule as ScheduleInputFilter;
use TdyCommons\Controller\BaseRestController;
use Zend\Filter\File\RenameUpload;
use Zend\Http\PhpEnvironment\Request;
use Zend\View\Model\JsonModel;

class SchedulesController extends BaseRestController
{
    protected $identifierName = 'id';

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

        $schedules = iterator_to_array($resultSet, false);

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

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

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

            ]
        );
    }

    /**
     * @param int|string $id
     *
     * @return JsonModel
     */
    public function get($id)
    {
        $schedule = $this->getSchedulesTable()->get($id);
        $schedule->setServiceLocator($this->getServiceLocator());

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

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

        /** @var Request $httpRequest */
        $httpRequest      = $this->getRequest();
        $contentTypeValue = $httpRequest->getHeaders()->get('Content-Type')->getFieldValue();

        list($contentType) = explode(';', trim($contentTypeValue));

        switch ($contentType) {
            case 'multipart/form-data':
                $file   = $httpRequest->getFiles('file');
                $filter = new RenameUpload(
                    [
                        'target'    => 'data/uploads/sheet.upd',
                        'randomize' => true,
                    ]
                );

                $file = $filter->filter($file);
                $path = $file['tmp_name'];

                $valid  = false;
                $reader = null;
                $types  = ['Excel2007', 'Excel5', 'CSV'];
                foreach ($types as $type) {
                    $reader = \PHPExcel_IOFactory::createReader($type);
                    if ($reader->canRead($path)) {
                        $valid = true;
                        break;
                    }
                }

                if ($valid) {
                    /** @var \PHPExcel $objPHPExcel */
                    $objPHPExcel = $reader->load($path);
                    $worksheet   = $objPHPExcel->getActiveSheet();

                    $i         = 1;
                    $j         = 1;
                    $schedules = [];

                    foreach ($worksheet->getRowIterator() as $row) {
                        $cellIterator = $row->getCellIterator();
                        $cellIterator->setIterateOnlyExistingCells(false);

                        if ($i == 1 && $worksheet->getCell('A1')->getValue() == 'sport') {
                            $i++;
                            continue;
                        }

                        $sportSlug     = $worksheet->getCell('A' . $i)->getValue();
                        $leagueSlug    = $worksheet->getCell('B' . $i)->getValue();
                        $seasonSlug    = $worksheet->getCell('C' . $i)->getValue();
                        $weekSlug      = $worksheet->getCell('D' . $i)->getValue();
                        $teamHomeSlug  = $worksheet->getCell('E' . $i)->getValue();
                        $teamHomeScore = $worksheet->getCell('F' . $i)->getValue();
                        $teamAwaySlug  = $worksheet->getCell('G' . $i)->getValue();
                        $teamAwayScore = $worksheet->getCell('H' . $i)->getValue();

                        $sport = $this->getSportsTable()->getBySlug($sportSlug);

                        $league   = $this->getLeaguesTable()->getBySlug($sport->id, $leagueSlug);
                        $season   = $this->getSeasonsTable()->getBySlug($league->id, $seasonSlug);
                        $week     = $this->getWeeksTable()->getBySlug($season->id, $weekSlug);
                        $teamHome = $this->getTeamsTable()->getByCode($league->id, $teamHomeSlug);
                        $teamAway = $this->getTeamsTable()->getByCode($league->id, $teamAwaySlug);

                        $schedule            = new Schedule();
                        $schedule->sportId   = $sport->id;
                        $schedule->leagueId  = $league->id;
                        $schedule->seasonId  = $season->id;
                        $schedule->weekId    = $week->id;
                        $schedule->teamHome  = $teamHome->id;
                        $schedule->scoreHome = $teamHomeScore;
                        $schedule->teamAway  = $teamAway->id;
                        $schedule->scoreAway = $teamAwayScore;

                        $newObject        = $this->getSchedulesTable()->save($schedule);
                        $newObject->rowNo = $j;
                        $schedules[]      = $newObject->toStdClass();

                        $i++;
                        $j++;
                    }

                    unlink($path);

                    return $this->statusOk(
                        [
                            'entries'    => $schedules,
                            'pagination' => [
                                'pageNumber' => 1,
                                'pageSize'   => $i,
                                'totalItems' => $i,
                                'totalPages' => 1,
                            ],
                        ]
                    );

                } else {
                    return $this->statusBadRequest(
                        [
                            'system'    => [
                                'id'          => 4002,
                                'description' => 'Invalid Import File',
                            ],
                            'schedules' => [
                                'description' => 'The file for import is invalid. Only accepts files in CSV, XLS, or XLSX format.',
                            ],
                        ]
                    );
                }


                break;
            case 'application/json':
            case 'text/json':
            default:
                $data['id'] = 0;

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

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

                    $data = $inputFilter->getValues();

                    $schedule            = new Schedule($data);
                    $schedule->status    = Schedule::STATUS_UPCOMING;
                    $schedule->ending    = 0;
                    $schedule->createdBy = $this->getCurrentAdmin()->id;

                    $schedule = $this->getSchedulesTable()->setIndent(self::LOGGER_INDENT)->save($schedule);

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

                    $this->getLogger()->info(self::LOGGER_INDENT . 'Schedule ' . $schedule->name . ' has been created.');
                    $this->getLogger()->debug('SchedulesController::create() end.');

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

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


    }

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

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

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

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

            $schedule            = new Schedule($data);
            $schedule->updatedBy = $this->getCurrentAdmin()->id;
            $schedule            = $this->getSchedulesTable()->setIndent(self::LOGGER_INDENT)->save($schedule);

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

            $this->getLogger()->info(self::LOGGER_INDENT . 'Schedule ' . $schedule->name . ' has been updated.');
            $this->getLogger()->debug('SchedulesController::update() end.');

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

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

    public function patchList($data)
    {

        $action = isset($data['action']) ? $data['action'] : '';

        switch ($action) {
            case 'update-scores':
                return $this->updateScores($data['info'], $data['items']);
            default:
                return $this->statusBadRequest(
                    [
                        'system'    => [
                            'id'          => 4001,
                            'description' => 'Invalid Request',
                        ],
                        'companies' => [
                            'description' => 'Action ID is missing.',
                            'details'     => '',
                        ],
                    ]
                );
        }
    }

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

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

        if ($id > 0) {
            $schedule = $this->getSchedulesTable()->get($id);
            if ($schedule) {

                $this->getLogger()->debug(self::LOGGER_INDENT . 'Schedule ID  : ' . $id);
                $this->getLogger()->debug(self::LOGGER_INDENT . 'Schedule Name: ' . $schedule->name);

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

                $this->getLogger()->info(self::LOGGER_INDENT . 'Schedule ' . $schedule->name . ' has been deleted.');
                $this->getLogger()->debug('SchedulesController::delete() end.');

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

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

            return $this->statusNotFound();
        }
    }

    protected function updateScores($info, $items)
    {

        foreach ($items as $item) {
            $schedule = new Schedule($item);
            $this->getSchedulesTable()->updateScores($schedule);
        }

        $this->recalculate($info['sport-id'], $info['league-id'], $info['week-id']);

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

    protected function recalculate($sportId, $leagueId, $weekId)
    {
        $this->getLogger()->debug('SchedulesController::recalculate() start.');
        $connection = $this->getWeeksTable()->getTableGateway()->getAdapter()->getDriver()->getConnection();

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

        try {
            $f = [
                'sport_id'  => $sportId,
                'league_id' => $leagueId,
                'week_id'   => $weekId,
            ];

            /**
             * @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) {

                // Get schedule before the score is updated.
                $oldSchedule = $this->getSchedulesTable()->get($schedule->id);

                $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,
                    ]
                );

                $isScoresSame     = $oldSchedule->scoreHome == $schedule->scoreHome && $oldSchedule->scoreAway == $schedule->scoreAway;
                $isdifferenceSame = ($oldSchedule->scoreHome - $oldSchedule->scoreAway) == $difference;

                if (!($isScoresSame || $isdifferenceSame)) {
                    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('SchedulesController::recalculate() 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('SchedulesController::recalculate() end.');
        }


    }

    protected function updateStandings($info) {

    }

}
