package com.gnx.telemedicine.service;

import com.gnx.telemedicine.dto.patient_safety.SentinelEventRequestDto;
import com.gnx.telemedicine.dto.patient_safety.SentinelEventResponseDto;
import com.gnx.telemedicine.dto.patient_safety.SentinelEventUpdateRequestDto;
import com.gnx.telemedicine.mappers.SentinelEventMapper;
import com.gnx.telemedicine.model.*;
import com.gnx.telemedicine.model.enums.SentinelEventStatus;
import com.gnx.telemedicine.repository.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Instant;
import java.util.List;
import java.util.UUID;

@Service
@RequiredArgsConstructor
@Slf4j
public class SentinelEventReportingService {
    
    private final SentinelEventRepository sentinelEventRepository;
    private final SentinelEventMapper sentinelEventMapper;
    private final PatientRepository patientRepository;
    private final DoctorRepository doctorRepository;
    private final AppointmentRepository appointmentRepository;
    private final UserRepository userRepository;
    private final EmailNotificationService emailNotificationService;
    
    @Transactional(readOnly = true)
    public List<SentinelEventResponseDto> getAllSentinelEvents() {
        return sentinelEventRepository.findAll()
                .stream()
                .map(sentinelEventMapper::toSentinelEventResponseDto)
                .toList();
    }
    
    @Transactional(readOnly = true)
    public List<SentinelEventResponseDto> getSentinelEventsByStatus(SentinelEventStatus status) {
        return sentinelEventRepository.findByStatusOrderByOccurredAtDesc(status)
                .stream()
                .map(sentinelEventMapper::toSentinelEventResponseDto)
                .toList();
    }
    
    @Transactional(readOnly = true)
    public List<SentinelEventResponseDto> getActiveSentinelEvents() {
        return sentinelEventRepository.findAllActiveIncidents()
                .stream()
                .map(sentinelEventMapper::toSentinelEventResponseDto)
                .toList();
    }
    
    @Transactional(readOnly = true)
    public List<SentinelEventResponseDto> getSentinelEventsByTimeRange(Instant startDate, Instant endDate) {
        return sentinelEventRepository.findByOccurredAtBetween(startDate, endDate)
                .stream()
                .map(sentinelEventMapper::toSentinelEventResponseDto)
                .toList();
    }
    
    @Transactional
    public SentinelEventResponseDto reportSentinelEvent(String userEmail, SentinelEventRequestDto requestDto) {
        UserModel reportingUser = userRepository.findByEmail(userEmail)
                .orElseThrow(() -> new RuntimeException("User not found"));
        
        SentinelEvent event = sentinelEventMapper.toSentinelEvent(requestDto);
        event.setReportedBy(reportingUser);
        event.setStatus(SentinelEventStatus.REPORTED);
        
        if (requestDto.patientId() != null) {
            Patient patient = patientRepository.findById(requestDto.patientId())
                    .orElse(null);
            event.setPatient(patient);
        }
        
        if (requestDto.doctorId() != null) {
            Doctor doctor = doctorRepository.findById(requestDto.doctorId())
                    .orElse(null);
            event.setDoctor(doctor);
        }
        
        if (requestDto.appointmentId() != null) {
            Appointment appointment = appointmentRepository.findById(requestDto.appointmentId())
                    .orElse(null);
            event.setAppointment(appointment);
        }
        
        SentinelEvent saved = sentinelEventRepository.save(event);
        log.info("Sentinel event {} reported by user {}", saved.getId(), userEmail);
        
        // Notify administrators
        notifyAdministrators(saved);
        
        return sentinelEventMapper.toSentinelEventResponseDto(saved);
    }
    
    @Transactional
    public SentinelEventResponseDto updateSentinelEvent(String userEmail, UUID eventId, SentinelEventUpdateRequestDto requestDto) {
        SentinelEvent event = sentinelEventRepository.findById(eventId)
                .orElseThrow(() -> new IllegalArgumentException("Sentinel event not found"));
        
        UserModel user = userRepository.findByEmail(userEmail)
                .orElseThrow(() -> new RuntimeException("User not found"));
        
        if (requestDto.status() != null) {
            event.setStatus(requestDto.status());
            
            if (requestDto.status() == SentinelEventStatus.RESOLVED || requestDto.status() == SentinelEventStatus.CLOSED) {
                event.setResolvedAt(Instant.now());
                event.setResolvedBy(user);
            }
        }
        
        if (requestDto.investigationNotes() != null) {
            event.setInvestigationNotes(requestDto.investigationNotes());
        }
        
        if (requestDto.rootCauseAnalysis() != null) {
            event.setRootCauseAnalysis(requestDto.rootCauseAnalysis());
        }
        
        if (requestDto.correctiveAction() != null) {
            event.setCorrectiveAction(requestDto.correctiveAction());
        }
        
        SentinelEvent saved = sentinelEventRepository.save(event);
        log.info("Sentinel event {} updated by user {}", eventId, userEmail);
        return sentinelEventMapper.toSentinelEventResponseDto(saved);
    }
    
    @Transactional(readOnly = true)
    public SentinelEventResponseDto getSentinelEventById(UUID id) {
        SentinelEvent event = sentinelEventRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Sentinel event not found"));
        return sentinelEventMapper.toSentinelEventResponseDto(event);
    }
    
    public long countActiveIncidents() {
        return sentinelEventRepository.countActiveIncidents();
    }
    
    private void notifyAdministrators(SentinelEvent event) {
        try {
            List<UserModel> admins = userRepository.findByRole(com.gnx.telemedicine.model.enums.Role.ADMIN);
            
            String subject = "SENTINEL EVENT REPORTED - " + event.getEventType();
            String message = String.format(
                    "A sentinel event has been reported and requires immediate attention.\n\n" +
                    "Event Type: %s\n" +
                    "Severity: %s\n" +
                    "Description: %s\n" +
                    "Location: %s\n" +
                    "Occurred: %s\n" +
                    "Reported By: %s %s\n\n" +
                    "Please log in to review and investigate this event.",
                    event.getEventType(),
                    event.getSeverity(),
                    event.getDescription(),
                    event.getLocation(),
                    event.getOccurredAt(),
                    event.getReportedBy().getFirstName(),
                    event.getReportedBy().getLastName()
            );
            
            for (UserModel admin : admins) {
                emailNotificationService.sendEmail(admin.getEmail(), subject, message);
            }
            
            log.info("Sentinel event notifications sent to {} administrators", admins.size());
        } catch (Exception e) {
            log.error("Failed to send sentinel event notifications", e);
        }
    }
}

