package com.gnx.telemedicine.service;

import com.gnx.telemedicine.dto.medical.VitalSignsRequestDto;
import com.gnx.telemedicine.dto.medical.VitalSignsResponseDto;
import com.gnx.telemedicine.mappers.VitalSignsMapper;
import com.gnx.telemedicine.model.Appointment;
import com.gnx.telemedicine.model.VitalSigns;
import com.gnx.telemedicine.repository.AppointmentRepository;
import com.gnx.telemedicine.repository.PatientRepository;
import com.gnx.telemedicine.repository.UserRepository;
import com.gnx.telemedicine.repository.VitalSignsRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Instant;
import java.util.List;
import java.util.UUID;

@Service
@RequiredArgsConstructor
public class VitalSignsService {
    
    private final VitalSignsRepository vitalSignsRepository;
    private final VitalSignsMapper vitalSignsMapper;
    private final PatientRepository patientRepository;
    private final AppointmentRepository appointmentRepository;
    private final UserRepository userRepository;

    @Transactional(readOnly = true)
    public List<VitalSignsResponseDto> getVitalSignsByPatientId(UUID patientId) {
        return vitalSignsRepository.findByPatientIdOrderByRecordedAtDesc(patientId)
                .stream()
                .map(vitalSignsMapper::toVitalSignsResponseDto)
                .toList();
    }

    @Transactional(readOnly = true)
    public VitalSignsResponseDto getLatestVitalSignsByPatientId(UUID patientId) {
        VitalSigns vitalSigns = vitalSignsRepository.findLatestByPatientId(patientId);
        if (vitalSigns == null) {
            return null; // Return null instead of throwing exception - let controller handle 404
        }
        return vitalSignsMapper.toVitalSignsResponseDto(vitalSigns);
    }

    @Transactional(readOnly = true)
    public List<VitalSignsResponseDto> getVitalSignsByAppointmentId(UUID appointmentId) {
        return vitalSignsRepository.findByAppointmentId(appointmentId)
                .stream()
                .map(vitalSignsMapper::toVitalSignsResponseDto)
                .toList();
    }

    @Transactional
    public VitalSignsResponseDto createVitalSigns(String userEmail, VitalSignsRequestDto requestDto) {
        // Get current user (recorder)
        var currentUser = userRepository.findByEmail(userEmail)
                .orElseThrow(() -> new RuntimeException("User not found"));

        // Validate and get patient
        var patient = patientRepository.findById(requestDto.patientId())
                .orElseThrow(() -> new IllegalArgumentException("Patient not found"));

        // Validate appointment if provided
        Appointment appointment = null;
        if (requestDto.appointmentId() != null) {
            appointment = appointmentRepository.findById(requestDto.appointmentId())
                    .orElseThrow(() -> new IllegalArgumentException("Appointment not found"));
        }

        // Create vital signs
        VitalSigns vitalSigns = vitalSignsMapper.toVitalSigns(requestDto);
        // Don't set ID manually - let Hibernate generate it via @GeneratedValue
        vitalSigns.setPatient(patient);
        vitalSigns.setAppointment(appointment);
        vitalSigns.setRecordedBy(currentUser);
        vitalSigns.setRecordedAt(Instant.now());

        // Calculate BMI if weight and height are provided
        if (requestDto.weightKg() != null && requestDto.heightCm() != null) {
            try {
                BigDecimal heightM = requestDto.heightCm().divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP);
                if (heightM.compareTo(BigDecimal.ZERO) > 0) {
                    BigDecimal bmi = requestDto.weightKg()
                        .divide(heightM.multiply(heightM), 1, RoundingMode.HALF_UP);
                    // Clamp BMI to column limits (precision 4, scale 1 => max 999.9)
                    if (bmi.compareTo(new BigDecimal("999.9")) > 0) {
                        bmi = new BigDecimal("999.9");
                    } else if (bmi.compareTo(BigDecimal.ZERO) < 0) {
                        bmi = BigDecimal.ZERO;
                    }
                    vitalSigns.setBmi(bmi);
                }
            } catch (ArithmeticException ex) {
                // Skip BMI if invalid inputs
                vitalSigns.setBmi(null);
            }
        }

        // Normalize numeric fields to match DB precision/scale and reasonable ranges
        if (vitalSigns.getTemperature() != null) {
            BigDecimal t = vitalSigns.getTemperature().setScale(1, RoundingMode.HALF_UP);
            // Acceptable human range roughly -50..60 C
            if (t.compareTo(new BigDecimal("-50.0")) < 0) t = new BigDecimal("-50.0");
            if (t.compareTo(new BigDecimal("60.0")) > 0) t = new BigDecimal("60.0");
            vitalSigns.setTemperature(t);
        }
        if (vitalSigns.getOxygenSaturation() != null) {
            BigDecimal o2 = vitalSigns.getOxygenSaturation().setScale(1, RoundingMode.HALF_UP);
            if (o2.compareTo(BigDecimal.ZERO) < 0) o2 = BigDecimal.ZERO;
            if (o2.compareTo(new BigDecimal("100.0")) > 0) o2 = new BigDecimal("100.0");
            vitalSigns.setOxygenSaturation(o2);
        }
        if (vitalSigns.getWeightKg() != null) {
            BigDecimal w = vitalSigns.getWeightKg().setScale(2, RoundingMode.HALF_UP);
            if (w.compareTo(BigDecimal.ZERO) < 0) w = BigDecimal.ZERO;
            vitalSigns.setWeightKg(w);
        }
        if (vitalSigns.getHeightCm() != null) {
            BigDecimal h = vitalSigns.getHeightCm().setScale(2, RoundingMode.HALF_UP);
            if (h.compareTo(BigDecimal.ZERO) < 0) h = BigDecimal.ZERO;
            vitalSigns.setHeightCm(h);
        }

        VitalSigns saved = vitalSignsRepository.save(vitalSigns);
        return vitalSignsMapper.toVitalSignsResponseDto(saved);
    }

    @Transactional(readOnly = true)
    public VitalSignsResponseDto getVitalSignsById(UUID id) {
        VitalSigns vitalSigns = vitalSignsRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Vital signs not found"));
        return vitalSignsMapper.toVitalSignsResponseDto(vitalSigns);
    }

    @Transactional
    public void deleteVitalSigns(UUID id) {
        vitalSignsRepository.deleteById(id);
    }
}

