package com.magistr.app.controller;

import com.magistr.app.config.auth.RequireRoles;
import com.magistr.app.dto.SubgroupDto;
import com.magistr.app.model.Role;
import com.magistr.app.model.StudentGroup;
import com.magistr.app.model.Subgroup;
import com.magistr.app.repository.GroupRepository;
import com.magistr.app.repository.ScheduleRuleSlotRepository;
import com.magistr.app.repository.SubgroupRepository;
import com.magistr.app.service.ScheduleGeneratorService;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

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

@RestController
@RequireRoles({Role.ADMIN, Role.EDUCATION_OFFICE, Role.STUDENT})
public class SubgroupController {

    private final SubgroupRepository subgroupRepository;
    private final GroupRepository groupRepository;
    private final ScheduleRuleSlotRepository scheduleRuleSlotRepository;
    private final ScheduleGeneratorService scheduleGeneratorService;

    public SubgroupController(SubgroupRepository subgroupRepository,
                              GroupRepository groupRepository,
                              ScheduleRuleSlotRepository scheduleRuleSlotRepository,
                              ScheduleGeneratorService scheduleGeneratorService) {
        this.subgroupRepository = subgroupRepository;
        this.groupRepository = groupRepository;
        this.scheduleRuleSlotRepository = scheduleRuleSlotRepository;
        this.scheduleGeneratorService = scheduleGeneratorService;
    }

    @GetMapping("/api/subgroups")
    public List<SubgroupDto> getAll() {
        return subgroupRepository.findAllWithGroupsOrderByGroupNameAndName().stream()
                .map(this::toDto)
                .toList();
    }

    @GetMapping("/api/groups/{groupId}/subgroups")
    public ResponseEntity<?> getByGroup(@PathVariable Long groupId) {
        if (!groupRepository.existsById(groupId)) {
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.ok(subgroupRepository.findByStudentGroupIdOrderByNameAsc(groupId).stream()
                .map(this::toDto)
                .toList());
    }

    @PostMapping("/api/groups/{groupId}/subgroups")
    @RequireRoles({Role.ADMIN, Role.EDUCATION_OFFICE})
    @Transactional
    public ResponseEntity<?> create(@PathVariable Long groupId, @RequestBody SubgroupDto request) {
        StudentGroup group = groupRepository.findByIdForUpdate(groupId).orElse(null);
        if (group == null) {
            return ResponseEntity.notFound().build();
        }
        String validationError = validate(request);
        if (validationError != null) {
            return ResponseEntity.badRequest().body(Map.of("message", validationError));
        }
        if (subgroupRepository.existsByStudentGroupIdAndNameIgnoreCase(groupId, request.name().trim())) {
            return ResponseEntity.badRequest().body(Map.of("message", "Подгруппа с таким названием уже есть в группе"));
        }
        String capacityError = validateGroupCapacity(group, null, request.studentCapacity());
        if (capacityError != null) {
            return ResponseEntity.badRequest().body(Map.of("message", capacityError));
        }

        Subgroup subgroup = new Subgroup();
        subgroup.setStudentGroup(group);
        subgroup.setName(request.name().trim());
        subgroup.setStudentCapacity(request.studentCapacity());
        Subgroup saved = subgroupRepository.save(subgroup);
        scheduleGeneratorService.clearCache();
        return ResponseEntity.ok(toDto(saved));
    }

    @PutMapping("/api/groups/{groupId}/subgroups/{id}")
    @RequireRoles({Role.ADMIN, Role.EDUCATION_OFFICE})
    @Transactional
    public ResponseEntity<?> update(@PathVariable Long groupId,
                                    @PathVariable Long id,
                                    @RequestBody SubgroupDto request) {
        StudentGroup group = groupRepository.findByIdForUpdate(groupId).orElse(null);
        if (group == null) {
            return ResponseEntity.notFound().build();
        }
        Subgroup subgroup = subgroupRepository.findByIdAndStudentGroupId(id, groupId).orElse(null);
        if (subgroup == null) {
            return ResponseEntity.notFound().build();
        }
        String validationError = validate(request);
        if (validationError != null) {
            return ResponseEntity.badRequest().body(Map.of("message", validationError));
        }
        String newName = request.name().trim();
        boolean duplicateName = subgroupRepository.existsByStudentGroupIdAndNameIgnoreCase(groupId, newName)
                && !subgroup.getName().equalsIgnoreCase(newName);
        if (duplicateName) {
            return ResponseEntity.badRequest().body(Map.of("message", "Подгруппа с таким названием уже есть в группе"));
        }
        String capacityError = validateGroupCapacity(group, id, request.studentCapacity());
        if (capacityError != null) {
            return ResponseEntity.badRequest().body(Map.of("message", capacityError));
        }

        subgroup.setName(newName);
        subgroup.setStudentCapacity(request.studentCapacity());
        Subgroup saved = subgroupRepository.save(subgroup);
        scheduleGeneratorService.clearCache();
        return ResponseEntity.ok(toDto(saved));
    }

    @DeleteMapping("/api/groups/{groupId}/subgroups/{id}")
    @RequireRoles({Role.ADMIN, Role.EDUCATION_OFFICE})
    @Transactional
    public ResponseEntity<?> delete(@PathVariable Long groupId, @PathVariable Long id) {
        if (groupRepository.findByIdForUpdate(groupId).isEmpty()) {
            return ResponseEntity.notFound().build();
        }
        Subgroup subgroup = subgroupRepository.findByIdAndStudentGroupId(id, groupId).orElse(null);
        if (subgroup == null) {
            return ResponseEntity.notFound().build();
        }
        if (scheduleRuleSlotRepository.existsBySubgroupId(id)) {
            return ResponseEntity.badRequest().body(Map.of("message", "Подгруппа используется в расписании"));
        }
        String deleteValidationError = validateDeleteKeepsCompleteSplit(subgroup);
        if (deleteValidationError != null) {
            return ResponseEntity.badRequest().body(Map.of("message", deleteValidationError));
        }
        subgroup.archive("Подгруппа архивирована");
        subgroupRepository.save(subgroup);
        scheduleGeneratorService.clearCache();
        return ResponseEntity.ok(Map.of("message", "Подгруппа архивирована"));
    }

    private String validate(SubgroupDto request) {
        if (request == null || request.name() == null || request.name().isBlank()) {
            return "Название подгруппы обязательно";
        }
        if (request.studentCapacity() == null) {
            return "Численность подгруппы обязательна";
        }
        if (request.studentCapacity() <= 0) {
            return "Численность подгруппы должна быть больше 0";
        }
        return null;
    }

    private String validateGroupCapacity(StudentGroup group, Long excludedSubgroupId, Integer requestedCapacity) {
        Long currentCapacity = excludedSubgroupId == null
                ? subgroupRepository.sumActiveStudentCapacityByGroupId(group.getId())
                : subgroupRepository.sumActiveStudentCapacityByGroupIdExcludingId(group.getId(), excludedSubgroupId);
        long totalCapacity = currentCapacity + requestedCapacity.longValue();
        if (totalCapacity > group.getGroupSize()) {
            return "Сумма численностей подгрупп не может превышать численность группы (" + group.getGroupSize() + " чел.)";
        }
        return null;
    }

    private String validateDeleteKeepsCompleteSplit(Subgroup subgroup) {
        StudentGroup group = subgroup.getStudentGroup();
        long remainingCount = subgroupRepository.countActiveByGroupIdExcludingId(group.getId(), subgroup.getId());
        if (remainingCount == 0) {
            return null;
        }

        Long remainingCapacity = subgroupRepository.sumActiveStudentCapacityByGroupIdExcludingId(
                group.getId(),
                subgroup.getId()
        );
        if (remainingCapacity < group.getGroupSize()) {
            return "Нельзя удалить одну подгруппу из деления: измените количество подгрупп в форме настройки или отключите деление";
        }
        return null;
    }

    private SubgroupDto toDto(Subgroup subgroup) {
        StudentGroup group = subgroup.getStudentGroup();
        return new SubgroupDto(
                subgroup.getId(),
                group.getId(),
                group.getName(),
                subgroup.getName(),
                subgroup.getStudentCapacity()
        );
    }
}
