package ru.ulstu.is.cbapp.service;

import javax.persistence.EntityManager;
import javax.persistence.EntityNotFoundException;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;

import org.springframework.data.util.Pair;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import ru.ulstu.is.cbapp.models.Category;
import ru.ulstu.is.cbapp.models.DrivingSchool;
import ru.ulstu.is.cbapp.models.Student;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

@Service
public class DrivingSchoolService {
    @PersistenceContext
    private EntityManager em;

    @Transactional
    public DrivingSchool addDrivingSchool(String name) {
        if (!StringUtils.hasText(name)) {
            throw new IllegalArgumentException("Student name is null or empty");
        }
        final DrivingSchool drivingSchool = new DrivingSchool(name);
        em.persist(drivingSchool);
        return drivingSchool;
    }

    @Transactional(readOnly = true)
    public DrivingSchool findDrivingSchool(Long id) {
        final DrivingSchool drivingSchool = em.find(DrivingSchool.class, id);
        if (drivingSchool == null) {
            throw new EntityNotFoundException(String.format("DrivingSchool with id [%s] is not found", id));
        }
        return drivingSchool;
    }

    @Transactional(readOnly = true)
    public DrivingSchool findDrivingSchool(String name) {
        DrivingSchool drivingSchool = em.createQuery("select d from DrivingSchool d WHERE d.name = :drivingSchoolName", DrivingSchool.class)
                .setParameter("drivingSchoolName",name)
                .getSingleResult();
        if (drivingSchool == null) {
            throw new EntityNotFoundException(String.format("DrivingSchool with name [%s] is not found", name));
        }
        return drivingSchool;
    }

    @Transactional(readOnly = true)
    public List<DrivingSchool> findAllDrivingSchools() {
        return em.createQuery("select d from DrivingSchool d", DrivingSchool.class)
                .getResultList();
    }

    @Transactional
    public DrivingSchool updateDrivingSchool(Long id, String name) {
        if (!StringUtils.hasText(name)) {
            throw new IllegalArgumentException("DrivingSchool name is null or empty");
        }
        final DrivingSchool currentDrivingSchool = findDrivingSchool(id);
        currentDrivingSchool.setName(name);

        return em.merge(currentDrivingSchool);
    }

    @Transactional
    public DrivingSchool deleteDrivingSchool(Long id) {
        final DrivingSchool currentDrivingSchool = findDrivingSchool(id);
        em.remove(currentDrivingSchool);
        return currentDrivingSchool;
    }

    @Transactional
    public void deleteAllDrivingSchools() {
        em.createQuery("delete from DrivingSchool").executeUpdate();
    }

    @Transactional
    public void addNewStudent(Long id, Student student) {
        DrivingSchool currentDrivingSchool = findDrivingSchool(id);
        currentDrivingSchool.addNewStudent(student);
        em.merge(currentDrivingSchool);
    }

    @Transactional
    public List<Pair<Long, Integer>> CountStudentsCategory(Long dsID) {
        TypedQuery<Object[]> query = em.createQuery("SELECT sc.category.Id, COUNT(sc.category.Id) AS count " +
                        "FROM StudentCategory sc " +
                        "JOIN sc.student s " +
                        "JOIN s.drivingSchool ds " +
                        "WHERE ds.Id=:dsID " +
                        "GROUP BY sc.category.Id", Object[].class)
                .setParameter("dsID", dsID);

        List<Object[]> resultList = query.getResultList();
        List<Pair<Long, Integer>> pairs = new ArrayList<>();

        for (Object[] result : resultList) {
            Long categoryId = (Long) result[0];
            Integer count = ((Number) result[1]).intValue();
            Pair<Long, Integer> pair = Pair.of(categoryId, count);
            pairs.add(pair);
        }
        return pairs;
    }

    @Transactional
    public void addAllStudent(Long id, List<Student> students) {
        DrivingSchool currentDrivingSchool = findDrivingSchool(id);
        for(int i=0; i<students.size(); i++) {
            currentDrivingSchool.addNewStudent(students.get(i));
            em.merge(currentDrivingSchool);
        }
    }

    @Transactional
    public void deleteStudent(Long id, Student student) {
        DrivingSchool currentDrivingSchool = findDrivingSchool(id);
        currentDrivingSchool.deleteStudent(student);
        em.merge(student);
    }
}
