package com.magistr.app.controller;

import com.magistr.app.service.ScheduleConflictException;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import java.time.Instant;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.NoSuchElementException;

@RestControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    private final DatabaseConstraintViolationMapper constraintViolationMapper =
            new DatabaseConstraintViolationMapper();

    @ExceptionHandler(ScheduleConflictException.class)
    public ResponseEntity<Map<String, Object>> handleScheduleConflict(ScheduleConflictException exception,
                                                                      HttpServletRequest request) {
        String message = exception.getMessage() == null || exception.getMessage().isBlank()
                ? "Изменение создаёт конфликт расписания"
                : exception.getMessage();
        return error(HttpStatus.CONFLICT, message, request);
    }

    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<Map<String, Object>> handleIllegalArgument(IllegalArgumentException exception,
                                                                     HttpServletRequest request) {
        String message = exception.getMessage() == null || exception.getMessage().isBlank()
                ? "Некорректный запрос"
                : exception.getMessage();
        return error(HttpStatus.BAD_REQUEST, message, request);
    }

    @ExceptionHandler(NoSuchElementException.class)
    public ResponseEntity<Map<String, Object>> handleNotFound(NoSuchElementException exception,
                                                              HttpServletRequest request) {
        String message = exception.getMessage() == null || exception.getMessage().isBlank()
                ? "Запрошенная запись не найдена"
                : exception.getMessage();
        return error(HttpStatus.NOT_FOUND, message, request);
    }

    @ExceptionHandler({
            HttpMessageNotReadableException.class,
            MethodArgumentTypeMismatchException.class
    })
    public ResponseEntity<Map<String, Object>> handleBadRequest(Exception exception,
                                                                HttpServletRequest request) {
        log.warn("Некорректный запрос {} {}: {}", request.getMethod(), request.getRequestURI(), exception.getMessage());
        return error(HttpStatus.BAD_REQUEST, "Некорректные параметры запроса", request);
    }

    @ExceptionHandler(DataIntegrityViolationException.class)
    public ResponseEntity<Map<String, Object>> handleDataIntegrityViolation(
            DataIntegrityViolationException exception,
            HttpServletRequest request) {
        DatabaseConstraintViolationMapper.ViolationResponse violation =
                constraintViolationMapper.map(exception);
        log.warn(
                "Нарушение целостности данных {} {}: constraint={}, sqlState={}",
                request.getMethod(),
                request.getRequestURI(),
                violation.constraintName() == null ? "не определено" : violation.constraintName(),
                violation.sqlState() == null ? "не определено" : violation.sqlState()
        );
        log.debug("Технические детали нарушения целостности данных", exception);
        return error(violation.status(), violation.message(), request);
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<Map<String, Object>> handleUnexpected(Exception exception,
                                                               HttpServletRequest request) {
        log.error("Необработанная ошибка {} {}", request.getMethod(), request.getRequestURI(), exception);
        return error(HttpStatus.INTERNAL_SERVER_ERROR, "Внутренняя ошибка сервера", request);
    }

    private ResponseEntity<Map<String, Object>> error(HttpStatus status, String message, HttpServletRequest request) {
        Map<String, Object> body = new LinkedHashMap<>();
        body.put("timestamp", Instant.now().toString());
        body.put("status", status.value());
        body.put("error", reason(status));
        body.put("message", message);
        body.put("path", request.getRequestURI());
        return ResponseEntity.status(status).body(body);
    }

    private String reason(HttpStatus status) {
        return switch (status) {
            case BAD_REQUEST -> "Некорректный запрос";
            case CONFLICT -> "Конфликт";
            case NOT_FOUND -> "Не найдено";
            case INTERNAL_SERVER_ERROR -> "Внутренняя ошибка сервера";
            default -> status.name();
        };
    }
}
