package ru.soune.nocopy.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Component;
import ru.soune.nocopy.dto.BaseRequest;
import ru.soune.nocopy.dto.BaseResponse;
import ru.soune.nocopy.dto.MessageCode;
import ru.soune.nocopy.dto.violation.ViolationRequest;
import ru.soune.nocopy.dto.violation.ViolationResponse;
import ru.soune.nocopy.entity.file.FileEntity;
import ru.soune.nocopy.entity.violation.Violation;
import ru.soune.nocopy.repository.FileEntityRepository;
import ru.soune.nocopy.service.complaint.ComplaintEntityService;
import ru.soune.nocopy.service.complaint.LawCaseService;
import ru.soune.nocopy.service.file.FileEntityService;
import ru.soune.nocopy.service.geo.GeoCountryService;
import ru.soune.nocopy.service.register.AuthService;
import ru.soune.nocopy.service.violation.ViolationService;
import ru.soune.nocopy.service.violation.ViolationStatus;

import java.io.FileNotFoundException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

@Component
@Slf4j
@RequiredArgsConstructor
public class ViolationHandler implements RequestHandler {
    private final ObjectMapper objectMapper;

    private final ViolationService violationService;

    private final FileEntityRepository fileRepository;

    private final AuthService authService;

    private final FileEntityService fileEntityService;

    private final GeoCountryService geoCountryService;

    private final LawCaseService lawCaseService;

    private final ComplaintEntityService complaintEntityService;

    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Override
    public BaseResponse handle(BaseRequest request) throws Exception {
        ViolationRequest violationRequest = objectMapper.convertValue(request.getMessageBody(), ViolationRequest.class);

        validateRequest(violationRequest);

        String token = violationRequest.getToken();
        Long userId = authService.useUserAuthToken(token);

        List<FileEntity> targetFiles;

        if (violationRequest.getFileId() != null && !violationRequest.getFileId().isEmpty()) {
            FileEntity file = fileRepository.findById(violationRequest.getFileId())
                    .orElseThrow(() -> new FileNotFoundException("File not found with id: " + violationRequest.getFileId()));
            targetFiles = List.of(file);
        } else {
            targetFiles = fileEntityService.getAllUserFiles(userId);
        }

        LocalDateTime startDate = null;
        LocalDateTime endDate = null;

        if (violationRequest.getStartDate() != null && violationRequest.getEndDate() != null) {
            startDate = parseDate(violationRequest.getStartDate());
            endDate = parseDate(violationRequest.getEndDate());
        }

        if ("updateStatus".equals(violationRequest.getAction())) {
            String status = violationRequest.getStatus();
            Long violationId = violationRequest.getViolationId();

            if (status.equals(ViolationStatus.NOT_OWNER_FILE.name()) &&
                    lawCaseService.fetchByViolationId(violationId) != null &&
                    complaintEntityService.fetchComplaintByViolation(violationId) != null) {

                return BaseResponse.builder()
                        .messageCode(MessageCode.COMPLAINT_OR_LAW_CASE_IN_WORK.getCode())
                        .messageCode(MessageCode.COMPLAINT_OR_LAW_CASE_IN_WORK.getCode())
                        .msgId(request.getMsgId())
                        .messageBody(Map.of("status", status))
                        .build();
            }

            violationService.changeStatus(ViolationStatus.valueOf(status), violationId);

            return BaseResponse.builder()
                    .msgId(request.getMsgId())
                    .messageBody(Map.of("status", status))
                    .build();
        }

        if ("group".equalsIgnoreCase(violationRequest.getAction())) {
            Map<String, Long> groupedData = violationService.getGroupedViolations(
                    targetFiles,
                    violationRequest.getGroupBy(),
                    violationRequest.getStatus(),
                    startDate,
                    endDate,
                    violationRequest.getSortDirection());

            return BaseResponse.builder()
                    .msgId(request.getMsgId())
                    .messageBody(groupedData)
                    .build();
        } else {
            Page<Violation> violationPage;

            if (startDate != null && endDate != null) {
                violationPage = violationService.getViolationsByFilesAndDateRange(
                        targetFiles, startDate, endDate,
                        violationRequest.getPage(), violationRequest.getSize(), violationRequest.getSortDirection()
                );
            } else if (violationRequest.getStatus() != null && !violationRequest.getStatus().isEmpty()) {
                violationPage = violationService.getViolationsByFilesAndStatus(
                        targetFiles, violationRequest.getStatus(),
                        violationRequest.getPage(), violationRequest.getSize(), violationRequest.getSortDirection()
                );
            } else {
                violationPage = violationService.getViolationsByFiles(
                        targetFiles, violationRequest.getPage(), violationRequest.getSize(),
                        violationRequest.getSortDirection());
            }

            ViolationResponse.ViolationDto firstShowViolation = null;
            List<Violation> content = violationPage.getContent();
            List<ViolationResponse.ViolationDto> violationDtos = new ArrayList<>();

            if ("getByViolationId".equals(violationRequest.getAction())) {
                Violation violation = violationService.getById(violationRequest.getViolationId());
                if (violation != null) {
                    firstShowViolation = ViolationResponse.ViolationDto.fromEntity(violation);
                    violationDtos.add(firstShowViolation);
                }
            }

            for (Violation violation : content) {
                if (firstShowViolation != null && Objects.equals(firstShowViolation.getId(), violation.getId())) continue;

                ViolationResponse.ViolationDto violationDto = ViolationResponse.ViolationDto.fromEntity(violation);
                violationDto.setCountry(geoCountryService.getCountryName(violation.getPageUrl()));
                violationDto.setCountryCode(geoCountryService.getCountryCode(violation.getPageUrl()));

                violationDtos.add(violationDto);
            }

            ViolationResponse response = ViolationResponse.builder()
                    .violations(violationDtos)
                    .totalElements(violationPage.getTotalElements())
                    .totalPages(violationPage.getTotalPages())
                    .currentPage(violationPage.getNumber())
                    .pageSize(violationPage.getSize())
                    .hasNext(violationPage.hasNext())
                    .hasPrevious(violationPage.hasPrevious())
                    .build();

            return BaseResponse.builder()
                    .msgId(request.getMsgId())
                    .messageBody(response)
                    .build();
        }
    }

    private void validateRequest(ViolationRequest request) {
        if (request.getPage() < 0) {
            request.setPage(0);
        }

        if (request.getSize() <= 0 || request.getSize() > 100) {
            request.setSize(10);
        }

        if (!request.getSortDirection().equalsIgnoreCase("asc") &&
                !request.getSortDirection().equalsIgnoreCase("desc")) {
            request.setSortDirection("desc");
        }
    }

    private LocalDateTime parseDate(String dateStr) {
        try {
            return LocalDateTime.parse(dateStr, DATE_FORMATTER);
        } catch (DateTimeParseException e) {
            log.error(e.getMessage());
            return null;
        }
    }
}
