package com.erp.systemtool.service;

import com.erp.systemtool.entity.AuditLog;
import com.erp.systemtool.repository.AuditLogRepository;
import jakarta.persistence.criteria.Predicate;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;

/**
 * Audit Log Service
 * 
 * Manages audit logs for compliance and security tracking.
 * Migrations from PHP AuditLogController.
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AuditLogService {

    private final AuditLogRepository auditLogRepository;

    /**
     * Query audit logs with filters
     */
    public Map<String, Object> queryLogs(Integer tenantId, Map<String, Object> params) {
        int page = params.get("page") != null ? (int) params.get("page") : 1;
        int limit = params.get("limit") != null ? (int) params.get("limit") : 20;

        Pageable pageable = PageRequest.of(page - 1, limit, Sort.by(Sort.Direction.DESC, "createdAt"));

        Specification<AuditLog> spec = (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            predicates.add(cb.equal(root.get("tenantId"), tenantId));

            if (params.containsKey("module") && params.get("module") != null) {
                predicates.add(cb.equal(root.get("module"), params.get("module")));
            }
            if (params.containsKey("action") && params.get("action") != null) {
                predicates.add(cb.equal(root.get("action"), params.get("action")));
            }
            if (params.containsKey("userId") && params.get("userId") != null) {
                predicates.add(cb.equal(root.get("userId"), params.get("userId")));
            }
            if (params.containsKey("targetType") && params.get("targetType") != null) {
                predicates.add(cb.equal(root.get("targetType"), params.get("targetType")));
            }
            if (params.containsKey("targetId") && params.get("targetId") != null) {
                predicates.add(cb.equal(root.get("targetId"), params.get("targetId")));
            }
            if (params.containsKey("startDate") && params.get("startDate") != null) {
                LocalDateTime start = LocalDate.parse((String) params.get("startDate")).atStartOfDay();
                predicates.add(cb.greaterThanOrEqualTo(root.get("createdAt"), start));
            }
            if (params.containsKey("endDate") && params.get("endDate") != null) {
                LocalDateTime end = LocalDate.parse((String) params.get("endDate")).atTime(LocalTime.MAX);
                predicates.add(cb.lessThanOrEqualTo(root.get("createdAt"), end));
            }
            return cb.and(predicates.toArray(new Predicate[0]));
        };

        Page<AuditLog> pageResult = auditLogRepository.findAll(spec, pageable);

        Map<String, Object> result = new HashMap<>();
        result.put("list", pageResult.getContent());
        result.put("total", pageResult.getTotalElements());
        result.put("current_page", pageResult.getNumber() + 1);
        result.put("last_page", pageResult.getTotalPages());
        return result;
    }

    /**
     * Get audit log by ID
     */
    public Optional<AuditLog> getLogById(Long id, Integer tenantId) {
        return auditLogRepository.findById(id)
            .filter(log -> log.getTenantId().equals(tenantId));
    }

    /**
     * Get audit log statistics
     */
    public Map<String, Object> getStats(Integer tenantId, String startDate, String endDate) {
        LocalDateTime start = startDate != null ? 
            LocalDate.parse(startDate).atStartOfDay() : 
            LocalDateTime.now().minusDays(7);
        LocalDateTime end = endDate != null ? 
            LocalDate.parse(endDate).atTime(LocalTime.MAX) : 
            LocalDateTime.now();

        List<Object[]> stats = auditLogRepository.statsByModuleAndAction(tenantId, start, end);

        // Group by module
        Map<String, Map<String, Long>> grouped = new LinkedHashMap<>();
        for (Object[] row : stats) {
            String module = (String) row[0];
            String action = (String) row[1];
            Long count = ((Number) row[2]).longValue();
            
            grouped.computeIfAbsent(module, k -> new LinkedHashMap<>())
                   .put(action, count);
        }

        Map<String, Object> result = new HashMap<>();
        result.put("statistics", grouped);
        result.put("start_date", start.toLocalDate());
        result.put("end_date", end.toLocalDate());
        return result;
    }

    /**
     * Create audit log
     */
    @Transactional
    public AuditLog createLog(AuditLog auditLog) {
        return auditLogRepository.save(auditLog);
    }

    /**
     * Clear old audit logs
     */
    @Transactional
    public long clearOldLogs(Integer tenantId, Integer days) {
        LocalDateTime cutoffDate = LocalDateTime.now().minusDays(days != null ? days : 90);
        return auditLogRepository.deleteOlderThan(cutoffDate);
    }
}
