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

import cz.muni.fi.pa165.soccermanager.dto.LeagueDto;
import cz.muni.fi.pa165.soccermanager.dto.request.CreateLeagueRequest;
import cz.muni.fi.pa165.soccermanager.exception.DeleteEntityException;
import cz.muni.fi.pa165.soccermanager.exception.NameAlreadyExistsException;
import cz.muni.fi.pa165.soccermanager.mapper.LeagueMapper;
import cz.muni.fi.pa165.soccermanager.service.LeagueService;
import cz.muni.fi.pa165.soccermanager.entity.Team;
import cz.muni.fi.pa165.soccermanager.repository.LeagueRepository;
import java.util.Optional;
import javax.persistence.EntityNotFoundException;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

@Service
@Transactional
@RequiredArgsConstructor
public class LeagueServiceImpl implements LeagueService {

    private final LeagueRepository leagueRepository;
    private final LeagueMapper leagueMapper;

    @Override
    @PreAuthorize("hasAuthority('SCOPE_ADMIN')")
    public void update(Long id, LeagueDto leagueDTO) {
        final var l = leagueRepository.findById(id).orElseThrow(() -> {
            throw new EntityNotFoundException();
        });
        leagueMapper.mapOnUpdate(l, leagueDTO);
    }

    @Override
    @PreAuthorize("hasAuthority('SCOPE_ADMIN')")
    public void remove(Long id) {
        final var league = leagueRepository.findById(id).orElseThrow(() -> {
            throw new EntityNotFoundException();
        });
        if (!league.getMatches().isEmpty() || !league.getTeams().isEmpty()) {
            throw new DeleteEntityException("League is used in some another entities");
        }
        leagueRepository.deleteById(id);
    }

    @Override
    @Transactional(readOnly = true)
    @PreAuthorize("hasAnyAuthority('SCOPE_USER', 'SCOPE_ADMIN')")
    public Optional<LeagueDto> findLeagueById(Long leagueId) {
        return leagueRepository.findById(leagueId).map(leagueMapper::map);
    }

    @Override
    @Transactional(readOnly = true)
    @PreAuthorize("hasAnyAuthority('SCOPE_USER', 'SCOPE_ADMIN')")
    public Page<LeagueDto> getAllLeagues(int offset, int pageSize, String field) {
        return leagueRepository.findAll(PageRequest.of(offset, pageSize). withSort(Sort.by(field).descending())).map(leagueMapper::map);
    }

    @Override
    @PreAuthorize("hasAuthority('SCOPE_ADMIN')")
    public void resetLeague(Long leagueId) {
        final var league = leagueRepository.findById(leagueId).orElseThrow(() -> {
            throw new EntityNotFoundException();
        });
        for (Team team : league.getTeams()) {
            team.setPoints(0);
            team.getMatches().clear();
        }
        leagueRepository.saveAndFlush(league);
    }

    @Override
    @PreAuthorize("hasAuthority('SCOPE_ADMIN')")
    public Long createLeague(CreateLeagueRequest request) {
        if (leagueRepository.leagueNameExists(request.getName()).equals(Boolean.TRUE)) {
            throw new NameAlreadyExistsException("Name of the league must be unique");
        }
        return leagueRepository.saveAndFlush(leagueMapper.map(request)).getId();
    }
}
