package com.saas.tenant.service;

import com.saas.tenant.dto.request.CreatePatientRequest;
import com.saas.tenant.dto.request.UpdatePatientRequest;
import com.saas.tenant.dto.response.PatientResponse;
import com.saas.tenant.entity.Patient;
import com.saas.tenant.repository.PatientRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
@Slf4j
public class PatientService {

    private final PatientRepository patientRepository;

    @Transactional
    public PatientResponse createPatient(CreatePatientRequest request) {
        log.info("Creating new patient: {} {}", request.getFirstName(), request.getLastName());

        // Check if patient already exists by phone or email
        if (request.getPhoneNumber() != null
                && patientRepository.findByPhoneNumber(request.getPhoneNumber()).isPresent()) {
            throw new RuntimeException("Patient with phone number " + request.getPhoneNumber() + " already exists");
        }
        if (request.getEmail() != null && patientRepository.findByEmail(request.getEmail()).isPresent()) {
            throw new RuntimeException("Patient with email " + request.getEmail() + " already exists");
        }

        Patient patient = new Patient();
        patient.setFirstName(request.getFirstName());
        patient.setLastName(request.getLastName());
        patient.setPhoneNumber(request.getPhoneNumber());
        patient.setEmail(request.getEmail());
        patient.setGender(request.getGender());
        patient.setDateOfBirth(request.getDateOfBirth());
        patient.setInsuranceProvider(request.getInsuranceProvider());
        patient.setInsuranceNumber(request.getInsuranceNumber());
        patient.setEmergencyContact(request.getEmergencyContact());
        patient.setEmergencyContactPhone(request.getEmergencyContactPhone());
        patient.setAddress(request.getAddress());
        patient.setCity(request.getCity());
        patient.setPostalCode(request.getPostalCode());
        patient.setPreferredLanguage(request.getPreferredLanguage());
        patient.setPatientNotes(request.getPatientNotes());

        Patient savedPatient = patientRepository.save(patient);
        log.info("Patient created successfully with ID: {}", savedPatient.getId());

        return mapToResponse(savedPatient);
    }

    @Transactional
    public PatientResponse updatePatient(Long id, UpdatePatientRequest request) {
        log.info("Updating patient with ID: {}", id);

        Patient patient = patientRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("Patient not found with ID: " + id));

        if (request.getFirstName() != null)
            patient.setFirstName(request.getFirstName());
        if (request.getLastName() != null)
            patient.setLastName(request.getLastName());
        if (request.getPhoneNumber() != null)
            patient.setPhoneNumber(request.getPhoneNumber());
        if (request.getEmail() != null)
            patient.setEmail(request.getEmail());
        if (request.getGender() != null)
            patient.setGender(request.getGender());
        if (request.getDateOfBirth() != null)
            patient.setDateOfBirth(request.getDateOfBirth());
        if (request.getInsuranceProvider() != null)
            patient.setInsuranceProvider(request.getInsuranceProvider());
        if (request.getInsuranceNumber() != null)
            patient.setInsuranceNumber(request.getInsuranceNumber());
        if (request.getEmergencyContact() != null)
            patient.setEmergencyContact(request.getEmergencyContact());
        if (request.getEmergencyContactPhone() != null)
            patient.setEmergencyContactPhone(request.getEmergencyContactPhone());
        if (request.getAddress() != null)
            patient.setAddress(request.getAddress());
        if (request.getCity() != null)
            patient.setCity(request.getCity());
        if (request.getPostalCode() != null)
            patient.setPostalCode(request.getPostalCode());
        if (request.getAiGeneratedSummary() != null)
            patient.setAiGeneratedSummary(request.getAiGeneratedSummary());
        if (request.getEmotionalTonePattern() != null)
            patient.setEmotionalTonePattern(request.getEmotionalTonePattern());
        if (request.getFrequentIssues() != null)
            patient.setFrequentIssues(request.getFrequentIssues());
        if (request.getPreferredLanguage() != null)
            patient.setPreferredLanguage(request.getPreferredLanguage());
        if (request.getPatientNotes() != null)
            patient.setPatientNotes(request.getPatientNotes());

        Patient updatedPatient = patientRepository.save(patient);
        log.info("Patient updated successfully: {}", updatedPatient.getId());

        return mapToResponse(updatedPatient);
    }

    @Transactional(readOnly = true)
    public PatientResponse getPatientById(Long id) {
        log.info("Fetching patient with ID: {}", id);
        Patient patient = patientRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("Patient not found with ID: " + id));
        return mapToResponse(patient);
    }

    @Transactional(readOnly = true)
    public List<PatientResponse> getAllPatients() {
        log.info("Fetching all patients");
        return patientRepository.findAll().stream()
                .map(this::mapToResponse)
                .collect(Collectors.toList());
    }

    @Transactional(readOnly = true)
    public PatientResponse getPatientByPhone(String phoneNumber) {
        log.info("Fetching patient by phone: {}", phoneNumber);
        Patient patient = patientRepository.findByPhoneNumber(phoneNumber)
                .orElseThrow(() -> new RuntimeException("Patient not found with phone: " + phoneNumber));
        return mapToResponse(patient);
    }

    @Transactional(readOnly = true)
    public PatientResponse getPatientByEmail(String email) {
        log.info("Fetching patient by email: {}", email);
        Patient patient = patientRepository.findByEmail(email)
                .orElseThrow(() -> new RuntimeException("Patient not found with email: " + email));
        return mapToResponse(patient);
    }

    @Transactional(readOnly = true)
    public List<PatientResponse> searchPatientsByName(String name) {
        log.info("Searching patients by name: {}", name);
        return patientRepository.searchByName(name).stream()
                .map(this::mapToResponse)
                .collect(Collectors.toList());
    }

    @Transactional
    public void deletePatient(Long id) {
        log.info("Deleting patient with ID: {}", id);
        patientRepository.deleteById(id);
        log.info("Patient deleted successfully: {}", id);
    }

    private PatientResponse mapToResponse(Patient patient) {
        return PatientResponse.builder()
                .id(patient.getId())
                .firstName(patient.getFirstName())
                .lastName(patient.getLastName())
                .phoneNumber(patient.getPhoneNumber())
                .email(patient.getEmail())
                .gender(patient.getGender())
                .dateOfBirth(patient.getDateOfBirth())
                .insuranceProvider(patient.getInsuranceProvider())
                .insuranceNumber(patient.getInsuranceNumber())
                .emergencyContact(patient.getEmergencyContact())
                .emergencyContactPhone(patient.getEmergencyContactPhone())
                .address(patient.getAddress())
                .city(patient.getCity())
                .postalCode(patient.getPostalCode())
                .aiGeneratedSummary(patient.getAiGeneratedSummary())
                .emotionalTonePattern(patient.getEmotionalTonePattern())
                .frequentIssues(patient.getFrequentIssues())
                .preferredLanguage(patient.getPreferredLanguage())
                .patientNotes(patient.getPatientNotes())
                .createdAt(patient.getCreatedAt())
                .updatedAt(patient.getUpdatedAt())
                .build();
    }
}
