package com.magistr.app.controller;

import com.magistr.app.config.auth.RequireRoles;
import com.magistr.app.dto.CreateSpecialityRequest;
import com.magistr.app.dto.SpecialityResponse;
import com.magistr.app.dto.SpecialtyProfileDto;
import com.magistr.app.model.LifecycleEntity;
import com.magistr.app.model.Role;
import com.magistr.app.model.Speciality;
import com.magistr.app.model.SpecialtyProfile;
import com.magistr.app.repository.SpecialtiesRepository;
import com.magistr.app.repository.SpecialtyProfileRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

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

@RestController
@RequestMapping("/api/specialties")
@RequireRoles({Role.ADMIN})
public class SpecialityController {

    private static final Logger logger = LoggerFactory.getLogger(SpecialityController.class);

    private final SpecialtiesRepository specialtiesRepository;
    private final SpecialtyProfileRepository specialtyProfileRepository;

    public SpecialityController(SpecialtiesRepository specialtiesRepository,
                                SpecialtyProfileRepository specialtyProfileRepository) {
        this.specialtiesRepository = specialtiesRepository;
        this.specialtyProfileRepository = specialtyProfileRepository;
    }

    @GetMapping
    @RequireRoles({Role.ADMIN, Role.EDUCATION_OFFICE})
    public List<Speciality> getAllSpecialties(@RequestParam(defaultValue = "false") boolean includeArchived) {
        logger.info("Получен запрос на получение списка специальностей");
        try {
            List<Speciality> specialities = includeArchived
                    ? specialtiesRepository.findAll()
                    : specialtiesRepository.findByStatusNot(LifecycleEntity.STATUS_ARCHIVED);
            List<Speciality> response = specialities.stream()
                    .map( s -> new Speciality(
                            s.getId(),
                            s.getSpecialityName(),
                            s.getSpecialityCode()
                    ))
                    .toList();
            logger.info("Получено {} специальностей", response.size());
            return response;
        } catch (Exception e) {
            logger.error("Ошибка при получении списка специальностей: {}", e.getMessage(), e);
            throw e;
        }
    }

    @PostMapping
    public ResponseEntity<?> createSpeciality(@RequestBody CreateSpecialityRequest request) {
        logger.info("Получен запрос на создание специальности: name = {}, code = {}", request.getSpecialityName(), request.getSpecialityCode());

        try {
            if (request.getSpecialityName() == null || request.getSpecialityName().isBlank()) {
                String errorMessage = "Название специальности обязательно";
                logger.error("Ошибка валидации: {}", errorMessage);
                return ResponseEntity.badRequest().body(Map.of("message", errorMessage));
            }
            if (specialtiesRepository.findBySpecialityName(request.getSpecialityName().trim()).isPresent()) {
                String errorMessage = "Специальность с таким названием уже существует";
                logger.error("Ошибка валидации: {}", errorMessage);
                return ResponseEntity.badRequest().body(Map.of("message", errorMessage));
            }
            if (request.getSpecialityCode() == null || request.getSpecialityCode().isBlank()) {
                String errorMessage = "Код специальности обязателен";
                logger.error("Ошибка валидации: {}", errorMessage);
                return ResponseEntity.badRequest().body(Map.of("message", errorMessage));
            }
            if (specialtiesRepository.findBySpecialityCode(request.getSpecialityCode().trim()).isPresent()) {
                String errorMessage = "Специальность с таким кодом уже существует";
                logger.error("Ошибка валидации: {}", errorMessage);
                return ResponseEntity.badRequest().body(Map.of("message", errorMessage));
            }

            Speciality speciality = new Speciality();
            speciality.setSpecialityName(request.getSpecialityName());
            speciality.setSpecialityCode(request.getSpecialityCode());
            specialtiesRepository.save(speciality);
            SpecialtyProfile defaultProfile = new SpecialtyProfile();
            defaultProfile.setSpeciality(speciality);
            defaultProfile.setName("Без профиля");
            specialtyProfileRepository.save(defaultProfile);

            logger.info("Специальность успешно создана с ID: {}", speciality.getId());

            return ResponseEntity.ok(
                    new SpecialityResponse(
                            speciality.getId(),
                            speciality.getSpecialityName(),
                            speciality.getSpecialityCode()
                    )
            );
        } catch (Exception e) {
            logger.error("Ошибка при создании специальности", e);
            throw e;
        }
    }

    @PutMapping("/{id}")
    public ResponseEntity<?> updateSpeciality(@PathVariable Long id, @RequestBody CreateSpecialityRequest request) {
        logger.info("Получен запрос на обновление специальности с ID: {}", id);

        Speciality speciality = specialtiesRepository.findById(id).orElse(null);
        if (speciality == null) {
            logger.info("Специальность с ID - {} не найдена", id);
            return ResponseEntity.notFound().build();
        }

        try {
            if (request.getSpecialityName() == null || request.getSpecialityName().isBlank()) {
                String errorMessage = "Название специальности обязательно";
                logger.error("Ошибка валидации: {}", errorMessage);
                return ResponseEntity.badRequest().body(Map.of("message", errorMessage));
            }
            if (request.getSpecialityCode() == null || request.getSpecialityCode().isBlank()) {
                String errorMessage = "Код специальности обязателен";
                logger.error("Ошибка валидации: {}", errorMessage);
                return ResponseEntity.badRequest().body(Map.of("message", errorMessage));
            }

            String specialityName = request.getSpecialityName().trim();
            String specialityCode = request.getSpecialityCode().trim();
            if (specialtiesRepository.findBySpecialityName(specialityName)
                    .filter(existing -> !existing.getId().equals(id))
                    .isPresent()) {
                String errorMessage = "Специальность с таким названием уже существует";
                logger.error("Ошибка валидации: {}", errorMessage);
                return ResponseEntity.badRequest().body(Map.of("message", errorMessage));
            }
            if (specialtiesRepository.findBySpecialityCode(specialityCode)
                    .filter(existing -> !existing.getId().equals(id))
                    .isPresent()) {
                String errorMessage = "Специальность с таким кодом уже существует";
                logger.error("Ошибка валидации: {}", errorMessage);
                return ResponseEntity.badRequest().body(Map.of("message", errorMessage));
            }

            speciality.setSpecialityName(specialityName);
            speciality.setSpecialityCode(specialityCode);
            specialtiesRepository.save(speciality);

            logger.info("Специальность с ID - {} успешно обновлена", id);
            return ResponseEntity.ok(new SpecialityResponse(
                    speciality.getId(),
                    speciality.getSpecialityName(),
                    speciality.getSpecialityCode()
            ));
        } catch (Exception e) {
            logger.error("Ошибка при обновлении специальности с ID - {}", id, e);
            throw e;
        }
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<?> deleteSpeciality(@PathVariable Long id) {
        logger.info("Получен запрос на удаление специальности с ID: {}", id);
        Speciality speciality = specialtiesRepository.findById(id).orElse(null);
        if (speciality == null) {
            logger.info("Специальность с ID - {} не найдена", id);
            return ResponseEntity.notFound().build();
        }
        speciality.archive("Специальность архивирована");
        specialtiesRepository.save(speciality);
        logger.info("Специальность с ID - {} успешно архивирована", id);
        return ResponseEntity.ok(Map.of("message", "Специальность архивирована"));
    }

    @PostMapping("/{id}/restore")
    public ResponseEntity<?> restoreSpeciality(@PathVariable Long id) {
        Speciality speciality = specialtiesRepository.findById(id).orElse(null);
        if (speciality == null) {
            return ResponseEntity.notFound().build();
        }
        speciality.restore();
        specialtiesRepository.save(speciality);
        return ResponseEntity.ok(new SpecialityResponse(
                speciality.getId(),
                speciality.getSpecialityName(),
                speciality.getSpecialityCode()
        ));
    }

    @GetMapping("/profiles")
    @RequireRoles({Role.ADMIN, Role.EDUCATION_OFFICE})
    public List<SpecialtyProfileDto> getAllProfiles() {
        logger.info("Получен запрос на получение всех профилей обучения");
        return specialtyProfileRepository.findAll().stream()
                .filter(profile -> profile.getSpeciality().isActiveRecord())
                .sorted((a, b) -> a.getName().compareToIgnoreCase(b.getName()))
                .map(this::toProfileDto)
                .toList();
    }

    @GetMapping("/{specialtyId}/profiles")
    @RequireRoles({Role.ADMIN, Role.EDUCATION_OFFICE})
    public ResponseEntity<?> getProfiles(@PathVariable Long specialtyId) {
        if (!specialtiesRepository.existsById(specialtyId)) {
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.ok(specialtyProfileRepository.findBySpecialityIdOrderByNameAsc(specialtyId).stream()
                .map(this::toProfileDto)
                .toList());
    }

    @PostMapping("/{specialtyId}/profiles")
    public ResponseEntity<?> createProfile(@PathVariable Long specialtyId, @RequestBody SpecialtyProfileDto request) {
        Speciality speciality = specialtiesRepository.findById(specialtyId).orElse(null);
        if (speciality == null) {
            return ResponseEntity.notFound().build();
        }
        String validationError = validateProfile(specialtyId, null, request);
        if (validationError != null) {
            return ResponseEntity.badRequest().body(Map.of("message", validationError));
        }

        SpecialtyProfile profile = new SpecialtyProfile();
        profile.setSpeciality(speciality);
        profile.setName(request.name().trim());
        profile.setDescription(trimToNull(request.description()));
        return ResponseEntity.ok(toProfileDto(specialtyProfileRepository.save(profile)));
    }

    @PutMapping("/{specialtyId}/profiles/{profileId}")
    public ResponseEntity<?> updateProfile(@PathVariable Long specialtyId,
                                           @PathVariable Long profileId,
                                           @RequestBody SpecialtyProfileDto request) {
        SpecialtyProfile profile = specialtyProfileRepository.findById(profileId).orElse(null);
        if (profile == null || !profile.getSpeciality().getId().equals(specialtyId)) {
            return ResponseEntity.notFound().build();
        }
        String validationError = validateProfile(specialtyId, profileId, request);
        if (validationError != null) {
            return ResponseEntity.badRequest().body(Map.of("message", validationError));
        }

        profile.setName(request.name().trim());
        profile.setDescription(trimToNull(request.description()));
        return ResponseEntity.ok(toProfileDto(specialtyProfileRepository.save(profile)));
    }

    @DeleteMapping("/{specialtyId}/profiles/{profileId}")
    public ResponseEntity<?> deleteProfile(@PathVariable Long specialtyId, @PathVariable Long profileId) {
        SpecialtyProfile profile = specialtyProfileRepository.findById(profileId).orElse(null);
        if (profile == null || !profile.getSpeciality().getId().equals(specialtyId)) {
            return ResponseEntity.notFound().build();
        }
        specialtyProfileRepository.delete(profile);
        return ResponseEntity.ok(Map.of("message", "Профиль обучения удалён"));
    }

    private String validateProfile(Long specialtyId, Long profileId, SpecialtyProfileDto request) {
        if (request == null) {
            return "Передайте данные профиля";
        }
        if (request.name() == null || request.name().isBlank()) {
            return "Название профиля обязательно";
        }
        return specialtyProfileRepository.findBySpecialityIdAndName(specialtyId, request.name().trim())
                .filter(existing -> !existing.getId().equals(profileId))
                .map(existing -> "Профиль с таким названием уже существует")
                .orElse(null);
    }

    private SpecialtyProfileDto toProfileDto(SpecialtyProfile profile) {
        Speciality speciality = profile.getSpeciality();
        return new SpecialtyProfileDto(
                profile.getId(),
                speciality.getId(),
                speciality.getSpecialityCode(),
                speciality.getSpecialityName(),
                profile.getName(),
                profile.getDescription()
        );
    }

    private String trimToNull(String value) {
        if (value == null || value.isBlank()) {
            return null;
        }
        return value.trim();
    }
}
