package cz.muni.fi.pa165.soccermanager.service.impl;

import static java.lang.Math.abs;
import static java.lang.Math.round;

import cz.muni.fi.pa165.soccermanager.dto.MatchDto;
import cz.muni.fi.pa165.soccermanager.dto.MatchScoreDto;
import cz.muni.fi.pa165.soccermanager.dto.request.CreateMatchRequest;
import cz.muni.fi.pa165.soccermanager.dto.request.UpdateMatchDto;
import cz.muni.fi.pa165.soccermanager.entity.League;
import cz.muni.fi.pa165.soccermanager.exception.MissingEntityReference;
import cz.muni.fi.pa165.soccermanager.mapper.MatchMapper;
import cz.muni.fi.pa165.soccermanager.repository.LeagueRepository;
import cz.muni.fi.pa165.soccermanager.repository.TeamRepository;
import cz.muni.fi.pa165.soccermanager.service.MatchService;
import cz.muni.fi.pa165.soccermanager.repository.MatchRepository;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import java.util.stream.Collectors;
import javax.persistence.EntityNotFoundException;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PathVariable;

/**
 * @author Stanislav Koshchienko (uco: 506549)
 */
@Service
@Transactional
@RequiredArgsConstructor
public class MatchServiceImpl implements MatchService {

    private final MatchRepository matchRepository;
    private final MatchMapper matchMapper;
    private final TeamRepository teamRepository;
    private final LeagueRepository leagueRepository;

    @Override
    @PreAuthorize("hasAuthority('SCOPE_ADMIN')")
    public void update(Long id, UpdateMatchDto matchdto) {
        final var m = matchRepository.findById(id).orElseThrow(() -> {
            throw new EntityNotFoundException();
        });
        matchMapper.mapOnUpdate(m, matchdto);
    }

    @Override
    public void remove(Long match) {
        matchRepository.delete(matchRepository.findById(match).orElseThrow(() -> {
            throw new EntityNotFoundException();
        }));
    }

    @Override
    @PreAuthorize("hasAnyAuthority('SCOPE_USER', 'SCOPE_ADMIN')")
    public Long saveMatch(CreateMatchRequest request, Long leagueId) {
        final var next = matchMapper.map(request);
        next.setLeague(leagueRepository.findById(leagueId).orElseThrow(() -> {
            throw new EntityNotFoundException();
        }));
        return matchRepository.saveAndFlush(next).getId();
    }

    @Override
    @Transactional(readOnly = true)
    @PreAuthorize("hasAnyAuthority('SCOPE_USER', 'SCOPE_ADMIN')")
    public Optional<MatchDto> findMatchById(Long matchId) {
        return matchRepository.findById(matchId).map(matchMapper::map);
    }

    @Override
    public MatchScoreDto playMatch(Long match) {
        final var m = matchRepository.findById(match).orElseThrow(() -> {
            throw new EntityNotFoundException();
        });

        final var generator = new Random();

        m.setChallengerGoals(abs((int) round(generator.nextGaussian() * 5)));
        m.setDefenderGoals(abs((int) round(generator.nextGaussian() * 4)));
        final var score = new MatchScoreDto();
        score.setDefenderGoals(m.getDefenderGoals());
        score.setChallengerGoals(m.getChallengerGoals());
        if (m.getChallengerGoals()>m.getDefenderGoals()){
            m.getChallenger().setPoints(m.getChallenger().getPoints()+3);
        }
        else if(m.getChallengerGoals() < m.getDefenderGoals()){
            m.getDefender().setPoints(m.getDefender().getPoints()+3);
        }
        else{
            m.getChallenger().setPoints(m.getChallenger().getPoints()+1);
            m.getDefender().setPoints(m.getDefender().getPoints()+1);

        }
        return score;
    }

    @Override
    @Transactional(readOnly = true)
    @PreAuthorize("hasAnyAuthority('SCOPE_USER', 'SCOPE_ADMIN')")
    public List<MatchDto> findMatchesByLeague(Long leagueId) {
        return matchRepository.findByLeagueId(leagueId).stream().map(matchMapper::map).collect(Collectors.toList());
    }

    @Override
    @Transactional(readOnly = true)
    @PreAuthorize("hasAnyAuthority('SCOPE_USER', 'SCOPE_ADMIN')")
    public List<MatchDto> getMatchesByTeamId(Long teamId) {
        final var t = teamRepository.findById(teamId).orElseThrow(() -> {
            throw new EntityNotFoundException();
        });
        if (t.getLeague() == null) {
            throw  new MissingEntityReference(League.class);
        }
        return matchRepository.findMatchesByTeamIdAndLeagueId(t.getId(), t.getLeague().getId())
            .stream().map(matchMapper::map).collect(Collectors.toList());
    }
}
