package com.magistr.app.controller;

import com.magistr.app.config.auth.RequireRoles;
import com.magistr.app.dto.*;
import com.magistr.app.model.*;
import com.magistr.app.repository.*;
import com.magistr.app.service.AcademicPeriodService;
import com.magistr.app.service.ScheduleGeneratorService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Comparator;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/api/admin/calendar")
@RequireRoles({Role.ADMIN, Role.EDUCATION_OFFICE})
public class AcademicCalendarAdminController {

    private final AcademicYearRepository academicYearRepository;
    private final SemesterRepository semesterRepository;
    private final AcademicCalendarActivityTypeRepository activityTypeRepository;
    private final AcademicCalendarDayRepository calendarDayRepository;
    private final ScheduleGeneratorService scheduleGeneratorService;
    private final AcademicPeriodService academicPeriodService;

    public AcademicCalendarAdminController(AcademicYearRepository academicYearRepository,
                                           SemesterRepository semesterRepository,
                                           AcademicCalendarActivityTypeRepository activityTypeRepository,
                                           AcademicCalendarDayRepository calendarDayRepository,
                                           ScheduleGeneratorService scheduleGeneratorService,
                                           AcademicPeriodService academicPeriodService) {
        this.academicYearRepository = academicYearRepository;
        this.semesterRepository = semesterRepository;
        this.activityTypeRepository = activityTypeRepository;
        this.calendarDayRepository = calendarDayRepository;
        this.scheduleGeneratorService = scheduleGeneratorService;
        this.academicPeriodService = academicPeriodService;
    }

    @GetMapping("/years")
    public List<AcademicYearDto> getYears() {
        return academicYearRepository.findAll().stream()
                .sorted(Comparator.comparing(AcademicYear::getStartDate).reversed())
                .map(this::toAcademicYearDto)
                .toList();
    }

    @PostMapping("/years")
    public ResponseEntity<?> createYear(@RequestBody AcademicYearDto request) {
        return ResponseEntity.ok(toAcademicYearDto(academicPeriodService.createYear(request)));
    }

    @PutMapping("/years/{id}")
    public ResponseEntity<?> updateYear(@PathVariable Long id, @RequestBody AcademicYearDto request) {
        return ResponseEntity.ok(toAcademicYearDto(academicPeriodService.updateYear(id, request)));
    }

    @DeleteMapping("/years/{id}")
    public ResponseEntity<?> deleteYear(@PathVariable Long id) {
        academicPeriodService.deleteYear(id);
        return ResponseEntity.ok(Map.of("message", "Учебный год удалён"));
    }

    @GetMapping("/years/{academicYearId}/semesters")
    public List<SemesterDto> getSemesters(@PathVariable Long academicYearId) {
        return semesterRepository.findByAcademicYearIdOrderByStartDateAsc(academicYearId).stream()
                .map(this::toSemesterDto)
                .toList();
    }

    @PostMapping("/years/{academicYearId}/semesters")
    public ResponseEntity<?> createSemester(@PathVariable Long academicYearId, @RequestBody SemesterDto request) {
        return ResponseEntity.ok(toSemesterDto(academicPeriodService.createSemester(academicYearId, request)));
    }

    @PutMapping("/semesters/{id}")
    public ResponseEntity<?> updateSemester(@PathVariable Long id, @RequestBody SemesterDto request) {
        return ResponseEntity.ok(toSemesterDto(academicPeriodService.updateSemester(id, request)));
    }

    @GetMapping("/activity-types")
    public List<AcademicCalendarActivityTypeDto> getActivityTypes() {
        return activityTypeRepository.findAllByOrderByDisplayOrderAscCodeAsc().stream()
                .map(this::toActivityTypeDto)
                .toList();
    }

    @PostMapping("/activity-types")
    public ResponseEntity<?> createActivityType(@RequestBody AcademicCalendarActivityTypeDto request) {
        String validationError = validateActivityType(request);
        if (validationError != null) {
            return ResponseEntity.badRequest().body(Map.of("message", validationError));
        }
        if (activityTypeRepository.findByCode(request.code().trim()).isPresent()) {
            return ResponseEntity.badRequest().body(Map.of("message", "Код активности уже существует"));
        }
        AcademicCalendarActivityType activityType = new AcademicCalendarActivityType();
        applyActivityType(activityType, request);
        scheduleGeneratorService.clearCache();
        return ResponseEntity.ok(toActivityTypeDto(activityTypeRepository.save(activityType)));
    }

    @PutMapping("/activity-types/{id}")
    public ResponseEntity<?> updateActivityType(@PathVariable Long id,
                                                @RequestBody AcademicCalendarActivityTypeDto request) {
        AcademicCalendarActivityType activityType = activityTypeRepository.findById(id).orElse(null);
        if (activityType == null) {
            return ResponseEntity.notFound().build();
        }
        String validationError = validateActivityType(request);
        if (validationError != null) {
            return ResponseEntity.badRequest().body(Map.of("message", validationError));
        }
        if (activityTypeRepository.findByCode(request.code().trim())
                .filter(existing -> !existing.getId().equals(id))
                .isPresent()) {
            return ResponseEntity.badRequest().body(Map.of("message", "Код активности уже существует"));
        }
        applyActivityType(activityType, request);
        scheduleGeneratorService.clearCache();
        return ResponseEntity.ok(toActivityTypeDto(activityTypeRepository.save(activityType)));
    }

    @DeleteMapping("/activity-types/{id}")
    public ResponseEntity<?> deleteActivityType(@PathVariable Long id) {
        if (!activityTypeRepository.existsById(id)) {
            return ResponseEntity.notFound().build();
        }
        if (calendarDayRepository.existsByActivityTypeId(id)) {
            return ResponseEntity.badRequest().body(Map.of("message", "Код активности используется в календарных графиках"));
        }
        activityTypeRepository.deleteById(id);
        scheduleGeneratorService.clearCache();
        return ResponseEntity.ok(Map.of("message", "Код активности удалён"));
    }

    private String validateActivityType(AcademicCalendarActivityTypeDto request) {
        if (request == null) {
            return "Передайте код активности";
        }
        if (request.code() == null || request.code().isBlank()) {
            return "Код активности обязателен";
        }
        if (request.name() == null || request.name().isBlank()) {
            return "Название активности обязательно";
        }
        return null;
    }

    private void applyActivityType(AcademicCalendarActivityType activityType, AcademicCalendarActivityTypeDto request) {
        activityType.setCode(request.code().trim());
        activityType.setName(request.name().trim());
        activityType.setAllowSchedule(Boolean.TRUE.equals(request.allowSchedule()));
        activityType.setColorCode(request.colorCode() == null || request.colorCode().isBlank()
                ? "#64748b"
                : request.colorCode().trim());
        activityType.setDisplayOrder(request.displayOrder() == null ? 100 : request.displayOrder());
        activityType.setDescription(request.description() == null || request.description().isBlank()
                ? null
                : request.description().trim());
    }

    private AcademicYearDto toAcademicYearDto(AcademicYear year) {
        List<SemesterDto> semesters = semesterRepository.findByAcademicYearIdOrderByStartDateAsc(year.getId())
                .stream()
                .map(this::toSemesterDto)
                .toList();
        return new AcademicYearDto(year.getId(), year.getTitle(), year.getStartDate(), year.getEndDate(), semesters);
    }

    private SemesterDto toSemesterDto(Semester semester) {
        return new SemesterDto(
                semester.getId(),
                semester.getAcademicYear().getId(),
                semester.getAcademicYear().getTitle(),
                semester.getSemesterType(),
                semester.getStartDate(),
                semester.getEndDate()
        );
    }

    private AcademicCalendarActivityTypeDto toActivityTypeDto(AcademicCalendarActivityType activityType) {
        return new AcademicCalendarActivityTypeDto(
                activityType.getId(),
                activityType.getCode(),
                activityType.getName(),
                activityType.getAllowSchedule(),
                activityType.getColorCode(),
                activityType.getDisplayOrder(),
                activityType.getDescription()
        );
    }
}
