package com.magistr.app.controller;

import com.magistr.app.config.auth.AuthContext;
import com.magistr.app.config.auth.RequireRoles;
import com.magistr.app.dto.CreateTeacherCreationRequest;
import com.magistr.app.dto.CreateSubjectRequest;
import com.magistr.app.dto.SubjectCommentDto;
import com.magistr.app.dto.TeacherCreationRequestResponse;
import com.magistr.app.dto.UserResponse;
import com.magistr.app.model.*;
import com.magistr.app.repository.DepartmentRepository;
import com.magistr.app.repository.SubjectCommentRepository;
import com.magistr.app.repository.SubjectRepository;
import com.magistr.app.repository.TeacherCreationRequestRepository;
import com.magistr.app.repository.TeacherDepartmentAssignmentRepository;
import com.magistr.app.repository.UserRepository;
import com.magistr.app.service.ScheduleQueryService;
import com.magistr.app.service.SubjectImportService;
import com.magistr.app.service.TeacherDepartmentService;
import com.magistr.app.service.BusinessTimeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

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

@RestController
@RequestMapping("/api/department")
@RequireRoles({Role.ADMIN, Role.DEPARTMENT})
public class DepartmentWorkspaceController {

    private static final Long NO_DEPARTMENT_ID = Long.MIN_VALUE;

    private final SubjectRepository subjectRepository;
    private final SubjectCommentRepository subjectCommentRepository;
    private final UserRepository userRepository;
    private final DepartmentRepository departmentRepository;
    private final TeacherDepartmentAssignmentRepository teacherDepartmentAssignmentRepository;
    private final TeacherCreationRequestRepository teacherCreationRequestRepository;
    private final ScheduleQueryService scheduleQueryService;
    private final TeacherDepartmentService teacherDepartmentService;
    private final SubjectImportService subjectImportService;
    private final BusinessTimeService businessTime;

    public DepartmentWorkspaceController(SubjectRepository subjectRepository,
                                         SubjectCommentRepository subjectCommentRepository,
                                         UserRepository userRepository,
                                         DepartmentRepository departmentRepository,
                                         TeacherDepartmentAssignmentRepository teacherDepartmentAssignmentRepository,
                                         TeacherCreationRequestRepository teacherCreationRequestRepository,
                                         ScheduleQueryService scheduleQueryService,
                                         TeacherDepartmentService teacherDepartmentService,
                                         SubjectImportService subjectImportService) {
        this(subjectRepository, subjectCommentRepository, userRepository, departmentRepository,
                teacherDepartmentAssignmentRepository, teacherCreationRequestRepository,
                scheduleQueryService, teacherDepartmentService, subjectImportService,
                BusinessTimeService.systemDefault());
    }

    @Autowired
    public DepartmentWorkspaceController(SubjectRepository subjectRepository,
                                         SubjectCommentRepository subjectCommentRepository,
                                         UserRepository userRepository,
                                         DepartmentRepository departmentRepository,
                                         TeacherDepartmentAssignmentRepository teacherDepartmentAssignmentRepository,
                                         TeacherCreationRequestRepository teacherCreationRequestRepository,
                                         ScheduleQueryService scheduleQueryService,
                                         TeacherDepartmentService teacherDepartmentService,
                                         SubjectImportService subjectImportService,
                                         BusinessTimeService businessTime) {
        this.subjectRepository = subjectRepository;
        this.subjectCommentRepository = subjectCommentRepository;
        this.userRepository = userRepository;
        this.departmentRepository = departmentRepository;
        this.teacherDepartmentAssignmentRepository = teacherDepartmentAssignmentRepository;
        this.teacherCreationRequestRepository = teacherCreationRequestRepository;
        this.scheduleQueryService = scheduleQueryService;
        this.teacherDepartmentService = teacherDepartmentService;
        this.subjectImportService = subjectImportService;
        this.businessTime = businessTime;
    }

    @GetMapping("/subjects")
    public List<Subject> getSubjects(@RequestParam(required = false) Long departmentId) {
        Long effectiveDepartmentId = effectiveDepartmentId(departmentId);
        return subjectRepository.findByDepartmentIdAndStatusNot(effectiveDepartmentId, LifecycleEntity.STATUS_ARCHIVED);
    }

    @PostMapping("/subjects/import")
    public ResponseEntity<?> importSubjects(@RequestBody List<CreateSubjectRequest> requests) {
        if (requests == null || requests.isEmpty()) {
            return ResponseEntity.badRequest().body(Map.of("message", "Передайте список дисциплин для загрузки"));
        }
        Long requestedDepartmentId = requests.stream()
                .map(CreateSubjectRequest::getDepartmentId)
                .filter(id -> id != null && id > 0)
                .findFirst()
                .orElse(null);
        Long departmentId = effectiveDepartmentId(requestedDepartmentId);
        if (departmentId == null) {
            return ResponseEntity.badRequest().body(Map.of("message", "Кафедра обязательна"));
        }
        List<Subject> saved = subjectImportService.importSubjects(requests, departmentId);
        return ResponseEntity.ok(Map.of(
                "message", "Дисциплины загружены",
                "count", saved.size()
        ));
    }

    @GetMapping("/subjects/{subjectId}/comments")
    public ResponseEntity<?> getComments(@PathVariable Long subjectId) {
        Subject subject = subjectRepository.findById(subjectId).orElse(null);
        if (subject == null || !canAccessDepartment(subject.getDepartmentId())) {
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.ok(subjectCommentRepository.findBySubjectIdOrderByCreatedAtDesc(subjectId).stream()
                .map(this::toCommentDto)
                .toList());
    }

    @PostMapping("/subjects/{subjectId}/comments")
    public ResponseEntity<?> addComment(@PathVariable Long subjectId, @RequestBody Map<String, String> request) {
        Subject subject = subjectRepository.findById(subjectId).orElse(null);
        if (subject == null || !canAccessDepartment(subject.getDepartmentId())) {
            return ResponseEntity.notFound().build();
        }
        String commentText = request == null ? null : request.get("comment");
        if (commentText == null || commentText.isBlank()) {
            return ResponseEntity.badRequest().body(Map.of("message", "Комментарий обязателен"));
        }
        SubjectComment comment = new SubjectComment();
        comment.setCreatedAt(businessTime.now());
        comment.setSubject(subject);
        comment.setComment(commentText.trim());
        Long currentUserId = AuthContext.currentUserId();
        if (currentUserId != null) {
            userRepository.findById(currentUserId).ifPresent(comment::setAuthor);
        }
        return ResponseEntity.ok(toCommentDto(subjectCommentRepository.save(comment)));
    }

    @GetMapping("/teachers")
    public List<UserResponse> getTeachers(@RequestParam(required = false) Long departmentId) {
        Long effectiveDepartmentId = effectiveDepartmentId(departmentId);
        if (effectiveDepartmentId == null) {
            return userRepository.findByRoleAndStatusNot(Role.TEACHER, LifecycleEntity.STATUS_ARCHIVED).stream()
                    .map(user -> toUserResponse(
                            user,
                            teacherDepartmentService.findPrimaryDepartmentIdAtDate(
                                    user.getId(),
                                    businessTime.today()
                            ).orElse(user.getDepartmentId())
                    ))
                    .toList();
        }

        return teacherDepartmentService
                .findTeachersForDepartmentAtDate(effectiveDepartmentId, businessTime.today()).stream()
                .map(user -> toUserResponse(user, effectiveDepartmentId))
                .toList();
    }

    @PostMapping("/teachers/{teacherId}/assignments")
    @Transactional
    public ResponseEntity<?> addTeacherToDepartment(@PathVariable Long teacherId,
                                                    @RequestBody(required = false) Map<String, Object> request) {
        Long requestedDepartmentId = asLong(request == null ? null : request.get("departmentId"));
        Long departmentId = effectiveDepartmentId(requestedDepartmentId);
        if (departmentId == null) {
            return ResponseEntity.badRequest().body(Map.of("message", "Кафедра обязательна"));
        }
        Department department = departmentRepository.findById(departmentId).orElse(null);
        if (department == null || department.isArchivedRecord()) {
            return ResponseEntity.badRequest().body(Map.of("message", "Активная кафедра не найдена"));
        }
        User teacher = userRepository.findById(teacherId).orElse(null);
        if (teacher == null || teacher.getRole() != Role.TEACHER || teacher.isArchivedRecord()) {
            return ResponseEntity.badRequest().body(Map.of("message", "Активный преподаватель не найден"));
        }
        LocalDate today = businessTime.today();
        if (teacherDepartmentAssignmentRepository.existsActiveAssignment(teacherId, departmentId, today)) {
            return ResponseEntity.badRequest().body(Map.of("message", "Преподаватель уже привязан к этой кафедре"));
        }

        TeacherDepartmentAssignment assignment = new TeacherDepartmentAssignment();
        assignment.setTeacher(teacher);
        assignment.setDepartment(department);
        assignment.setValidFrom(today);
        assignment.setPrimaryAssignment(false);
        assignment.setComment(trimToNull(request == null ? null : request.get("comment")));
        assignment.setCreatedBy(AuthContext.currentUserId());
        teacherDepartmentAssignmentRepository.save(assignment);

        return ResponseEntity.ok(toAssignmentMap(assignment));
    }

    @GetMapping("/teacher-requests")
    public List<TeacherCreationRequestResponse> getTeacherRequests(@RequestParam(required = false) Long departmentId) {
        Long effectiveDepartmentId = effectiveDepartmentId(departmentId);
        if (effectiveDepartmentId == null) {
            return teacherCreationRequestRepository.findAllByOrderByCreatedAtDesc().stream()
                    .map(this::toTeacherCreationRequestResponse)
                    .toList();
        }
        return teacherCreationRequestRepository.findByDepartment_IdOrderByCreatedAtDesc(effectiveDepartmentId).stream()
                .map(this::toTeacherCreationRequestResponse)
                .toList();
    }

    @PostMapping("/teacher-requests")
    public ResponseEntity<?> createTeacherRequest(@RequestBody CreateTeacherCreationRequest request) {
        if (request == null) {
            return ResponseEntity.badRequest().body(Map.of("message", "Данные заявки обязательны"));
        }
        Long departmentId = effectiveDepartmentId(request == null ? null : request.getDepartmentId());
        if (departmentId == null) {
            return ResponseEntity.badRequest().body(Map.of("message", "Кафедра обязательна"));
        }
        Department department = departmentRepository.findById(departmentId).orElse(null);
        if (department == null || department.isArchivedRecord()) {
            return ResponseEntity.badRequest().body(Map.of("message", "Активная кафедра не найдена"));
        }
        String username = trimToNull(request.getUsername());
        String fullName = trimToNull(request.getFullName());
        String jobTitle = trimToNull(request.getJobTitle());
        if (username == null || fullName == null) {
            return ResponseEntity.badRequest().body(Map.of("message", "Логин и ФИО преподавателя обязательны"));
        }
        if (username.length() > 50 || fullName.length() > 255 || (jobTitle != null && jobTitle.length() > 255)) {
            return ResponseEntity.badRequest().body(Map.of("message", "Проверьте длину полей заявки"));
        }
        if (userRepository.findByUsername(username).isPresent()
                || teacherCreationRequestRepository.existsByUsernameAndStatus(username, TeacherCreationRequestStatus.PENDING)) {
            return ResponseEntity.badRequest().body(Map.of("message", "Логин уже занят или ожидает одобрения"));
        }

        TeacherCreationRequest teacherRequest = new TeacherCreationRequest();
        teacherRequest.setCreatedAt(businessTime.now());
        teacherRequest.setUpdatedAt(businessTime.now());
        teacherRequest.setDepartment(department);
        teacherRequest.setUsername(username);
        teacherRequest.setFullName(fullName);
        teacherRequest.setJobTitle(jobTitle == null ? "Не указано" : jobTitle);
        teacherRequest.setComment(trimToNull(request.getComment()));
        teacherRequest.setRequestedBy(AuthContext.currentUserId());
        return ResponseEntity.ok(toTeacherCreationRequestResponse(teacherCreationRequestRepository.save(teacherRequest)));
    }

    @GetMapping("/schedule")
    public ResponseEntity<?> getSchedule(
            @RequestParam(required = false) Long departmentId,
            @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
            @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate
    ) {
        Long effectiveDepartmentId = effectiveDepartmentId(departmentId);
        return ResponseEntity.ok(scheduleQueryService.search(null, null, null, effectiveDepartmentId,
                null, null, null, null, startDate, endDate));
    }

    private SubjectCommentDto toCommentDto(SubjectComment comment) {
        User author = comment.getAuthor();
        return new SubjectCommentDto(
                comment.getId(),
                comment.getSubject().getId(),
                author == null ? null : author.getId(),
                author == null ? null : author.getFullName(),
                comment.getComment(),
                comment.getCreatedAt()
        );
    }

    private UserResponse toUserResponse(User user, Long departmentId) {
        UserResponse response = new UserResponse(
                user.getId(),
                user.getUsername(),
                user.getRole().name(),
                user.getFullName(),
                user.getJobTitle(),
                departmentId
        );
        response.setStatus(user.getStatus());
        return response;
    }

    private TeacherCreationRequestResponse toTeacherCreationRequestResponse(TeacherCreationRequest request) {
        Department department = request.getDepartment();
        return new TeacherCreationRequestResponse(
                request.getId(),
                department == null ? null : department.getId(),
                department == null ? null : department.getDepartmentName(),
                request.getUsername(),
                request.getFullName(),
                request.getJobTitle(),
                request.getComment(),
                request.getStatus().name(),
                request.getRequestedBy(),
                request.getCreatedAt(),
                request.getReviewedBy(),
                request.getReviewedAt(),
                request.getReviewComment(),
                request.getCreatedTeacherId()
        );
    }

    private Map<String, Object> toAssignmentMap(TeacherDepartmentAssignment assignment) {
        Map<String, Object> response = new LinkedHashMap<>();
        response.put("id", assignment.getId());
        response.put("teacherId", assignment.getTeacher().getId());
        response.put("teacherName", assignment.getTeacher().getFullName());
        response.put("departmentId", assignment.getDepartment().getId());
        response.put("departmentName", assignment.getDepartment().getDepartmentName());
        response.put("validFrom", assignment.getValidFrom());
        response.put("primaryAssignment", assignment.getPrimaryAssignment());
        return response;
    }

    private Long asLong(Object value) {
        if (value instanceof Number number) {
            return number.longValue();
        }
        if (value instanceof String text && !text.isBlank()) {
            try {
                return Long.parseLong(text);
            } catch (NumberFormatException ignored) {
                return null;
            }
        }
        return null;
    }

    private String trimToNull(Object value) {
        if (value == null) {
            return null;
        }
        String text = value.toString().trim();
        return text.isEmpty() ? null : text;
    }

    private Long effectiveDepartmentId(Long requestedDepartmentId) {
        var user = AuthContext.getCurrentUser();
        if (user != null && user.role() == Role.DEPARTMENT) {
            return user.departmentId() == null ? NO_DEPARTMENT_ID : user.departmentId();
        }
        return requestedDepartmentId == null && user != null ? user.departmentId() : requestedDepartmentId;
    }

    private boolean canAccessDepartment(Long departmentId) {
        var user = AuthContext.getCurrentUser();
        return user == null
                || user.role() == Role.ADMIN
                || (user.role() == Role.DEPARTMENT && Objects.equals(departmentId, user.departmentId()));
    }
}
