package com.gnx.telemedicine.service;

import com.gnx.telemedicine.model.HipaaAuditLog;
import com.gnx.telemedicine.model.Patient;
import com.gnx.telemedicine.model.UserModel;
import com.gnx.telemedicine.model.enums.ActionType;
import com.gnx.telemedicine.repository.HipaaAuditLogRepository;
import com.gnx.telemedicine.repository.PatientRepository;
import com.gnx.telemedicine.repository.UserRepository;
import jakarta.servlet.http.HttpServletRequest;
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.Map;
import java.util.UUID;

@Service
@RequiredArgsConstructor
@Slf4j
public class HipaaAuditService {
    
    private final HipaaAuditLogRepository hipaaAuditLogRepository;
    private final UserRepository userRepository;
    private final PatientRepository patientRepository;

    @Transactional
    public void logAccess(String userEmail, ActionType actionType, String resourceType, UUID resourceId, UUID patientId, Map<String, Object> details, HttpServletRequest request) {
        logAccess(userEmail, actionType, resourceType, resourceId, patientId, details, true, null, request);
    }

    @Transactional
    public void logAccess(String userEmail, ActionType actionType, String resourceType, UUID resourceId, UUID patientId, Map<String, Object> details, boolean success, String errorMessage, HttpServletRequest request) {
        try {
            UserModel user = userRepository.findByEmail(userEmail)
                    .orElseThrow(() -> new RuntimeException("User not found"));

            HipaaAuditLog auditLog = new HipaaAuditLog();
            auditLog.setUser(user);
            auditLog.setActionType(actionType);
            auditLog.setResourceType(resourceType);
            auditLog.setResourceId(resourceId);
            auditLog.setSuccess(success);
            auditLog.setErrorMessage(errorMessage);
            auditLog.setDetails(details);
            auditLog.setTimestamp(Instant.now());

            // Set patient if provided
            if (patientId != null) {
                Patient patient = patientRepository.findById(patientId).orElse(null);
                auditLog.setPatient(patient);
            }

            // Extract IP address and user agent from request
            if (request != null) {
                String ipAddress = getClientIpAddress(request);
                auditLog.setIpAddress(ipAddress);
                auditLog.setUserAgent(request.getHeader("User-Agent"));
            }

            hipaaAuditLogRepository.save(auditLog);
        } catch (Exception e) {
            // Log error but don't throw - audit logging should not break the main flow
            log.error("Failed to create HIPAA audit log: {}", e.getMessage(), e);
        }
    }

    @Transactional(readOnly = true)
    public List<HipaaAuditLog> getAuditLogsByPatientId(UUID patientId) {
        return hipaaAuditLogRepository.findByPatientIdOrderByTimestampDesc(patientId);
    }

    @Transactional(readOnly = true)
    public List<HipaaAuditLog> getAuditLogsByUserId(UUID userId) {
        return hipaaAuditLogRepository.findByUserIdOrderByTimestampDesc(userId);
    }

    @Transactional(readOnly = true)
    public List<HipaaAuditLog> getAuditLogsByResource(String resourceType, UUID resourceId) {
        return hipaaAuditLogRepository.findByResourceTypeAndResourceIdOrderByTimestampDesc(resourceType, resourceId);
    }

    @Transactional(readOnly = true)
    public List<HipaaAuditLog> getAuditLogsByPatientIdAndDateRange(UUID patientId, Instant startDate, Instant endDate) {
        return hipaaAuditLogRepository.findByPatientIdAndDateRange(patientId, startDate, endDate);
    }

    @Transactional(readOnly = true)
    public Long countAccessesByPatientIdSince(UUID patientId, Instant startDate) {
        return hipaaAuditLogRepository.countAccessesByPatientIdSince(patientId, startDate);
    }

    private String getClientIpAddress(HttpServletRequest request) {
        String ipAddress = request.getHeader("X-Forwarded-For");
        if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
        }
        // Handle multiple IPs (first one is the original client)
        if (ipAddress != null && ipAddress.contains(",")) {
            ipAddress = ipAddress.split(",")[0].trim();
        }
        return ipAddress;
    }
}

