package com.magistr.app.controller;

import com.magistr.app.config.auth.AuthContext;
import com.magistr.app.config.auth.RequireRoles;
import com.magistr.app.dto.CreateUserRequest;
import com.magistr.app.dto.DepartmentTransferRequest;
import com.magistr.app.dto.TeacherDepartmentAssignmentDto;
import com.magistr.app.dto.UserResponse;
import com.magistr.app.model.Department;
import com.magistr.app.model.LifecycleEntity;
import com.magistr.app.model.Role;
import com.magistr.app.model.TeacherDepartmentAssignment;
import com.magistr.app.model.User;
import com.magistr.app.repository.DepartmentRepository;
import com.magistr.app.repository.TeacherDepartmentAssignmentRepository;
import com.magistr.app.repository.UserRepository;
import com.magistr.app.service.TeacherDepartmentService;
import com.magistr.app.service.BusinessTimeService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDate;
import java.util.List;
import java.util.Map;

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

    private static final Logger logger = LoggerFactory.getLogger(UserController.class);
    private final UserRepository userRepository;
    private final DepartmentRepository departmentRepository;
    private final TeacherDepartmentAssignmentRepository teacherDepartmentAssignmentRepository;
    private final TeacherDepartmentService teacherDepartmentService;
    private final BCryptPasswordEncoder passwordEncoder;
    private final BusinessTimeService businessTime;

    public UserController(UserRepository userRepository,
                          BCryptPasswordEncoder passwordEncoder,
                          DepartmentRepository departmentRepository,
                          TeacherDepartmentAssignmentRepository teacherDepartmentAssignmentRepository,
                          TeacherDepartmentService teacherDepartmentService) {
        this(userRepository, passwordEncoder, departmentRepository,
                teacherDepartmentAssignmentRepository, teacherDepartmentService,
                BusinessTimeService.systemDefault());
    }

    @Autowired
    public UserController(UserRepository userRepository,
                          BCryptPasswordEncoder passwordEncoder,
                          DepartmentRepository departmentRepository,
                          TeacherDepartmentAssignmentRepository teacherDepartmentAssignmentRepository,
                          TeacherDepartmentService teacherDepartmentService,
                          BusinessTimeService businessTime) {
        this.userRepository = userRepository;
        this.passwordEncoder = passwordEncoder;
        this.departmentRepository = departmentRepository;
        this.teacherDepartmentAssignmentRepository = teacherDepartmentAssignmentRepository;
        this.teacherDepartmentService = teacherDepartmentService;
        this.businessTime = businessTime;
    }

    @GetMapping
    public List<UserResponse> getAllUsers(@RequestParam(defaultValue = "false") boolean includeArchived) {
        logger.info("Получен запрос на получение всех пользователей");
        List<User> users = includeArchived
                ? userRepository.findAll()
                : userRepository.findByStatusNot(LifecycleEntity.STATUS_ARCHIVED);
        List<UserResponse> response = users.stream()
                .map(this::toUserResponse)
                .toList();
        logger.info("Получено {} пользователей", response.size());
        return response;
    }

    @GetMapping("/teachers")
    @RequireRoles({Role.ADMIN, Role.EDUCATION_OFFICE, Role.DEPARTMENT, Role.SCHEDULE_VIEWER})
    public List<UserResponse> getTeachers() {
        logger.info("Запрос на получение пользователей с ролью 'Преподаватель'");
        List<UserResponse> response = userRepository.findByRoleAndStatusNot(Role.TEACHER, LifecycleEntity.STATUS_ARCHIVED).stream()
                .map(this::toUserResponse)
                .toList();
        logger.info("Получено {} преподавателей", response.size());
        return response;
    }

    @GetMapping("/teachers/{departmentId}")
    @RequireRoles({Role.ADMIN, Role.EDUCATION_OFFICE, Role.DEPARTMENT, Role.SCHEDULE_VIEWER})
    public ResponseEntity<?> getTeachersByDepartmentId(@PathVariable Long departmentId){
        if (!canAccessDepartment(departmentId)) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN)
                    .body(Map.of("message", "Недостаточно прав для просмотра преподавателей этой кафедры"));
        }
        logger.info("Получен запрос на получение преподавателей для кафедры с ID - {}", departmentId);
        List<User> users = teacherDepartmentService.findTeachersForDepartmentAtDate(
                departmentId,
                businessTime.today()
        );

        if (users.isEmpty()) {
            logger.info("Преподаватели для кафедры с ID - {} не найдены", departmentId);
            return ResponseEntity.status(HttpStatus.NOT_FOUND)
                    .body("Преподаватели для указанной кафедры не найдены");
        }

        logger.info("Найдено {} преподавателей для кафедры с ID - {}", users.size(), departmentId);
        return ResponseEntity.ok(users.stream()
                .map(user -> toUserResponse(user, departmentId))
                .toList());
    }

    @PostMapping
    public ResponseEntity<?> createUser(@RequestBody CreateUserRequest request) {
        if (request == null) {
            String errorMessage = "Данные пользователя обязательны";
            logger.error("Ошибка валидации: {}", errorMessage);
            return ResponseEntity.badRequest().body(Map.of("message", errorMessage));
        }
        logger.info("Получен запрос на создание нового пользователя: username = {}, fullName = {}, jobTitle = {}, departmentId = {}", request.getUsername(), request.getFullName(), request.getJobTitle(), request.getDepartmentId());

        if (request.getUsername() == null || request.getUsername().isBlank()) {
            String errorMessage = "Имя пользователя обязательно";
            logger.error("Ошибка валидации: {}", errorMessage);
            return ResponseEntity.badRequest().body(Map.of("message", errorMessage));
        }
        if (request.getPassword() == null || request.getPassword().length() < 8) {
            String errorMessage = "Пароль минимум 8 символов";
            logger.error("Ошибка валидации: {}", errorMessage);
            return ResponseEntity.badRequest().body(Map.of("message", errorMessage));
        }
        if (userRepository.findByUsername(request.getUsername()).isPresent()) {
            String errorMessage = "Пользователь уже существует";
            logger.error("Ошибка валидации: {}", errorMessage);
            return ResponseEntity.badRequest().body(Map.of("message", errorMessage));
        }
        if (request.getFullName() == null || request.getFullName().isBlank()) {
            String errorMessage = "Имя пользователя обязательно";
            logger.error("Ошибка валидации: {}", errorMessage);
            return ResponseEntity.badRequest().body(Map.of("message", errorMessage));
        }
        if (request.getJobTitle() == null || request.getJobTitle().isBlank()) {
            logger.info("Должность не была указана, установлено значение по умолчанию: 'Не указано'");
            request.setJobTitle("Не указано");
        }
        if (request.getDepartmentId() == null || request.getDepartmentId() == 0) {
            String errorMessage = "ID кафедры не может быть равен 0 или пустым";
            logger.error("Ошибка валидации: {}", errorMessage);
            return ResponseEntity.badRequest().body(Map.of("message", errorMessage));
        }
        Department requestedDepartment = departmentRepository.findById(request.getDepartmentId()).orElse(null);
        if (requestedDepartment == null || requestedDepartment.isArchivedRecord()) {
            String errorMessage = "Активная кафедра не найдена";
            logger.error("Ошибка валидации: {}", errorMessage);
            return ResponseEntity.badRequest().body(Map.of("message", errorMessage));
        }

        Role role;
        try {
            role = Role.valueOf(request.getRole());
        } catch (IllegalArgumentException | NullPointerException e) {
            logger.error("Ошибка при преобразовании роли: {}", e.getMessage());
            return ResponseEntity.badRequest().body(Map.of("message", "Недопустимая роль"));
        }

        User user = new User();
        user.setUsername(request.getUsername());
        user.setPassword(passwordEncoder.encode(request.getPassword()));
        user.setRole(role);
        user.setFullName(request.getFullName());
        user.setJobTitle(request.getJobTitle());
        user.setDepartmentId(request.getDepartmentId());
        userRepository.save(user);
        if (role == Role.TEACHER) {
            TeacherDepartmentAssignment assignment = new TeacherDepartmentAssignment();
            assignment.setTeacher(user);
            assignment.setDepartment(requestedDepartment);
            assignment.setValidFrom(businessTime.today());
            assignment.setPrimaryAssignment(true);
            assignment.setComment("Начальная кафедра при создании пользователя");
            assignment.setCreatedBy(AuthContext.currentUserId());
            teacherDepartmentAssignmentRepository.save(assignment);
        }

        logger.info("Пользователь успешно создан с ID: {}", user.getId());
        return ResponseEntity.ok(toUserResponse(user));
    }

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

    @PostMapping("/{id}/restore")
    public ResponseEntity<?> restoreUser(@PathVariable Long id) {
        User user = userRepository.findById(id).orElse(null);
        if (user == null) {
            return ResponseEntity.notFound().build();
        }
        user.restore();
        userRepository.save(user);
        return ResponseEntity.ok(toUserResponse(user));
    }

    @GetMapping("/{id}/department-history")
    @RequireRoles({Role.ADMIN, Role.EDUCATION_OFFICE, Role.DEPARTMENT})
    public ResponseEntity<?> getDepartmentHistory(@PathVariable Long id) {
        if (!userRepository.existsById(id)) {
            return ResponseEntity.notFound().build();
        }
        var currentUser = AuthContext.getCurrentUser();
        if (currentUser != null
                && currentUser.role() == Role.DEPARTMENT
                && !teacherDepartmentService.hasAssignmentAtDate(
                        id,
                        currentUser.departmentId(),
                        businessTime.today()
                )) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN)
                    .body(Map.of("message", "Недостаточно прав для просмотра истории этого преподавателя"));
        }
        return ResponseEntity.ok(teacherDepartmentAssignmentRepository.findHistoryByTeacherId(id).stream()
                .map(this::toAssignmentDto)
                .toList());
    }

    @PostMapping("/{id}/department-transfer")
    public ResponseEntity<?> transferTeacher(@PathVariable Long id, @RequestBody DepartmentTransferRequest request) {
        TeacherDepartmentAssignment assignment = teacherDepartmentService.transferTeacher(
                id,
                request,
                businessTime.today(),
                AuthContext.currentUserId()
        );
        return ResponseEntity.ok(toAssignmentDto(assignment));
    }

    @GetMapping("/teachers/by-department/{departmentId}")
    @RequireRoles({Role.ADMIN, Role.EDUCATION_OFFICE, Role.DEPARTMENT})
    public ResponseEntity<?> getTeachersByDepartmentAtDate(@PathVariable Long departmentId,
                                                           @RequestParam(required = false)
                                                           @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date) {
        if (!canAccessDepartment(departmentId)) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN)
                    .body(Map.of("message", "Недостаточно прав для просмотра преподавателей этой кафедры"));
        }
        LocalDate effectiveDate = date == null ? businessTime.today() : date;
        return ResponseEntity.ok(teacherDepartmentService
                .findTeachersForDepartmentAtDate(departmentId, effectiveDate).stream()
                .map(user -> toUserResponse(user, departmentId))
                .toList());
    }

    private UserResponse toUserResponse(User user) {
        Long departmentId = user.getRole() == Role.TEACHER
                ? teacherDepartmentService.findPrimaryDepartmentIdAtDate(user.getId(), businessTime.today())
                .orElse(user.getDepartmentId())
                : user.getDepartmentId();
        return toUserResponse(user, departmentId);
    }

    private UserResponse toUserResponse(User user, Long departmentId) {
        String departmentName = departmentId == null
                ? null
                : departmentRepository.findById(departmentId)
                .map(Department::getDepartmentName)
                .orElse("Неизвестно");
        UserResponse response = new UserResponse(
                user.getId(),
                user.getUsername(),
                user.getRole().name(),
                user.getFullName(),
                user.getJobTitle(),
                departmentName
        );
        response.setDepartmentId(departmentId);
        response.setStatus(user.getStatus());
        return response;
    }

    private TeacherDepartmentAssignmentDto toAssignmentDto(TeacherDepartmentAssignment assignment) {
        return new TeacherDepartmentAssignmentDto(
                assignment.getId(),
                assignment.getTeacher().getId(),
                assignment.getTeacher().getFullName(),
                assignment.getDepartment().getId(),
                assignment.getDepartment().getDepartmentName(),
                assignment.getValidFrom(),
                assignment.getValidTo(),
                assignment.getPrimaryAssignment(),
                assignment.getComment()
        );
    }

    private boolean canAccessDepartment(Long departmentId) {
        var currentUser = AuthContext.getCurrentUser();
        return currentUser == null
                || currentUser.role() != Role.DEPARTMENT
                || (departmentId != null && departmentId.equals(currentUser.departmentId()));
    }
}
