package ru.soune.nocopy.service.complaint;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import ru.soune.nocopy.dto.complaint.ComplaintRequest;
import ru.soune.nocopy.dto.complaint.ComplaintResponse;
import ru.soune.nocopy.entity.complaint.ComplaintEntity;
import ru.soune.nocopy.entity.complaint.ComplaintStatus;
import ru.soune.nocopy.entity.file.FileStatus;
import ru.soune.nocopy.entity.notification.NotificationType;
import ru.soune.nocopy.entity.violation.Violation;
import ru.soune.nocopy.exception.ComplaintNotFoundException;
import ru.soune.nocopy.exception.DuplicateComplaintException;
import ru.soune.nocopy.exception.ViolationNotFoundException;
import ru.soune.nocopy.repository.ComplaintEntityRepository;
import ru.soune.nocopy.repository.ViolationRepository;
import ru.soune.nocopy.service.notification.NotificationService;
import ru.soune.nocopy.service.violation.ViolationService;
import ru.soune.nocopy.service.violation.ViolationStatus;

import java.time.format.DateTimeFormatter;
import java.util.List;

@Slf4j
@Service
@RequiredArgsConstructor
@Transactional
public class ComplaintEntityService {
    private final ComplaintEntityRepository complaintRepository;

    private final ViolationRepository violationRepository;

    private final ViolationService violationService;

    private final NotificationService notificationService;

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

    public ComplaintResponse createComplaint(ComplaintRequest request) {
        Violation violation = violationRepository.findById(request.getViolationId())
                .orElseThrow(() -> new ViolationNotFoundException("Violation not found: " + request.getViolationId()));

        if (complaintRepository.existsByViolationId(request.getViolationId())) {
            throw new DuplicateComplaintException("Complaint already exists for violation: " + request.getViolationId());
        }

        ComplaintEntity complaint = ComplaintEntity.builder()
                .status(ComplaintStatus.CREATED)
                .complaintText(request.getComplaintText())
                .email(request.getEmail())
                .violation(violation)
                .not_moderated_file(violation.getFileEntity().getStatus().equals(FileStatus.MODERATION))
                .build();

        violationService.changeStatus(ViolationStatus.COMPLAINT_IN_WORK, request.getViolationId());

        return mapToResponse(complaintRepository.save(complaint));
    }

    @Transactional(readOnly = true)
    public Page<ComplaintResponse> getAllComplaintsByEmail(Pageable pageable, List<String> emails) {
        return complaintRepository.getComplaintsByUserEmails(emails, pageable)
                .map(this::mapToResponse);
    }

    @Transactional(readOnly = true)
    public ComplaintResponse getComplaintById(Long id) {
        return complaintRepository.findById(id)
                .map(this::mapToResponse)
                .orElseThrow(() -> new ComplaintNotFoundException("Complaint not found: " + id));
    }

    @Transactional(readOnly = true)
    public Page<ComplaintResponse> getAllComplaints(Pageable pageable) {
        return complaintRepository.findAll(pageable).map(this::mapToResponse);
    }

    @Transactional(readOnly = true)
    public ComplaintResponse getComplaintsByViolationId(Long violationId) {
        if (!violationRepository.existsById(violationId)) {
            throw new ViolationNotFoundException("Violation not found: " + violationId);
        }

        return mapToResponse(complaintRepository.findByViolationId(violationId));
    }

    public List<ComplaintEntity> fetchComplaintByViolation(Long violationId) {
        if (!violationRepository.existsById(violationId)) {
            return null;
        }

        return complaintRepository.findByViolationIdAndStatuses(violationId, List.of(ComplaintStatus.CREATED.name(),
                ComplaintStatus.SHOWED.name(), ComplaintStatus.IN_WORK.name()));
    }

    public ComplaintResponse updateComplaintStatus(ComplaintEntity complaint, ComplaintStatus status) {
        complaint.setStatus(status);

        return mapToResponse(complaintRepository.save(complaint));
    }

    public ComplaintResponse updateComplaint(Long id, ComplaintRequest request) {
        ComplaintEntity complaint = complaintRepository.findById(id)
                .orElseThrow(() -> new ComplaintNotFoundException("Complaint not found: " + id));

        if (request.getViolationId() != null) {
            Violation violation = violationRepository.findById(request.getViolationId())
                    .orElseThrow(() -> new ViolationNotFoundException("Violation not found: " + request.getViolationId()));
            complaint.setViolation(violation);
        }
        if (request.getComplaintText() != null) complaint.setComplaintText(request.getComplaintText());
        if (request.getEmail() != null) complaint.setEmail(request.getEmail());
        if (request.getStatus() != null) {
            complaint.setStatus(ComplaintStatus.valueOf(request.getStatus()));

            notificationService.addNotification(NotificationType.COMPLAINT_STATUS_CHANGED, complaint.getViolation()
                    .getFileEntity().getUserId(), request.getStatus());
        }

        return mapToResponse(complaintRepository.save(complaint));
    }

    public ComplaintResponse deleteComplaint(Long id) {
        ComplaintEntity complaint = complaintRepository.findById(id)
                .orElseThrow(() -> new ComplaintNotFoundException("Complaint not found: " + id));
        complaint.setStatus(ComplaintStatus.CANCELLED);
        return mapToResponse(complaintRepository.save(complaint));
    }

    private ComplaintResponse mapToResponse(ComplaintEntity entity) {
        Violation violation = entity.getViolation();

        return ComplaintResponse.builder()
                .id(entity.getId())
                .status(entity.getStatus() != null ? entity.getStatus().name() : null)
                .complaintText(entity.getComplaintText())
                .email(entity.getEmail())
                .violationId(violation != null ? violation.getId() : null)
                .fileId(violation != null ? violation.getFileEntity().getId() : null)
                .createdAt(entity.getCreatedAt() != null ? entity.getCreatedAt().format(DATE_FORMATTER) : null)
                .updatedAt(entity.getUpdatedAt() != null ? entity.getUpdatedAt().format(DATE_FORMATTER) : null)
                .notModerated(entity.getNot_moderated_file() != null ? entity.getNot_moderated_file() : null)
                .build();
    }
}
