package com.magistr.app.service;

import com.magistr.app.model.*;
import com.magistr.app.repository.AcademicCalendarDayRepository;
import com.magistr.app.repository.SemesterRepository;
import com.magistr.app.repository.StudentGroupCalendarAssignmentRepository;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.Optional;

@Service
public class AcademicDateService {

    private final SemesterRepository semesterRepository;
    private final AcademicCalendarDayRepository calendarDayRepository;
    private final StudentGroupCalendarAssignmentRepository assignmentRepository;
    private final boolean evenWeekIsUpper;

    public AcademicDateService(SemesterRepository semesterRepository,
                               AcademicCalendarDayRepository calendarDayRepository,
                               StudentGroupCalendarAssignmentRepository assignmentRepository,
                               @Value("${app.schedule.even-week-is-upper:false}") boolean evenWeekIsUpper) {
        this.semesterRepository = semesterRepository;
        this.calendarDayRepository = calendarDayRepository;
        this.assignmentRepository = assignmentRepository;
        this.evenWeekIsUpper = evenWeekIsUpper;
    }

    public Optional<Semester> findSemester(LocalDate date) {
        return semesterRepository.findFirstByStartDateLessThanEqualAndEndDateGreaterThanEqual(date, date);
    }

    public int getWeekNumber(Semester semester, LocalDate date) {
        long days = ChronoUnit.DAYS.between(semester.getStartDate(), date);
        if (days < 0) {
            return 0;
        }
        return (int) (days / 7) + 1;
    }

    public ScheduleParity getParity(Semester semester, LocalDate date) {
        int weekNumber = getWeekNumber(semester, date);
        boolean evenWeek = weekNumber % 2 == 0;
        if (evenWeekIsUpper) {
            return evenWeek ? ScheduleParity.ODD : ScheduleParity.EVEN;
        }
        return evenWeek ? ScheduleParity.EVEN : ScheduleParity.ODD;
    }

    public int getCourseNumber(StudentGroup group, LocalDate date) {
        int academicYearStart = date.getMonthValue() >= 9 ? date.getYear() : date.getYear() - 1;
        return academicYearStart - group.getYearStartStudy() + 1;
    }

    public Optional<AcademicCalendarActivityType> getActivityType(StudentGroup group, Semester semester, LocalDate date) {
        if (group == null || semester == null || semester.getAcademicYear() == null || date == null) {
            return Optional.empty();
        }

        int courseNumber = getCourseNumber(group, date);
        if (courseNumber < 1) {
            return Optional.empty();
        }

        return assignmentRepository.findForSchedule(group.getId(), semester.getAcademicYear().getId())
                .flatMap(assignment -> calendarDayRepository.findByCalendarIdAndCourseNumberAndDate(
                        assignment.getAcademicCalendar().getId(),
                        courseNumber,
                        date
                ))
                .map(AcademicCalendarDay::getActivityType);
    }

    public boolean isTheoryDay(StudentGroup group, Semester semester, LocalDate date) {
        return getActivityType(group, semester, date)
                .map(AcademicCalendarActivityType::getAllowSchedule)
                .orElse(false);
    }

    public boolean hasCalendarAssignment(StudentGroup group, Semester semester) {
        if (group == null || semester == null || semester.getAcademicYear() == null) {
            return false;
        }
        return assignmentRepository.findForSchedule(group.getId(), semester.getAcademicYear().getId()).isPresent();
    }
}
