/*
 * Records and retrieves user activity logs so admins can
 * see who did what and when.
 *
 * Author: Harkamal
 * Date: April 2026
 */
package com.petshop.backend.service;

import com.petshop.backend.dto.activity.ActivityLogResponse;
import com.petshop.backend.entity.ActivityLog;
import com.petshop.backend.entity.StoreLocation;
import com.petshop.backend.entity.User;
import com.petshop.backend.repository.ActivityLogRepository;
import com.petshop.backend.repository.UserRepository;
import com.petshop.backend.util.StringUtils;
import jakarta.persistence.criteria.Predicate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.PageRequest;
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.util.ArrayList;
import java.util.List;

@Service
public class ActivityLogService {

    private static final Logger log = LoggerFactory.getLogger(ActivityLogService.class);

    private final ActivityLogRepository activityLogRepository;
    private final UserRepository userRepository;

    public ActivityLogService(ActivityLogRepository activityLogRepository, UserRepository userRepository) {
        this.activityLogRepository = activityLogRepository;
        this.userRepository = userRepository;
    }

    @Transactional
    public void record(Long userId, String activity) {
        if (userId == null || activity == null || activity.isBlank()) {
            return;
        }

        try {
            User managedUser = userRepository.findById(userId).orElse(null);
            if (managedUser == null) {
                return;
            }
            StoreLocation store = managedUser.getPrimaryStore();
            ActivityLog entry = new ActivityLog();
            entry.setUser(managedUser);
            entry.setStore(store);
            entry.setUsernameSnapshot(managedUser.getUsername());
            String fullName = managedUser.getFullName() != null && !managedUser.getFullName().isBlank()
                    ? managedUser.getFullName()
                    : StringUtils.fullName(managedUser.getFirstName(), managedUser.getLastName());
            entry.setFullNameSnapshot(fullName);
            entry.setRoleSnapshot(managedUser.getRole() != null ? managedUser.getRole().name() : null);
            entry.setStoreNameSnapshot(store != null ? store.getStoreName() : null);
            entry.setActivity(activity.trim());
            activityLogRepository.save(entry);
            log.info("[ACTIVITY] {} | {} | {} | {}",
                    entry.getRoleSnapshot(),
                    entry.getUsernameSnapshot(),
                    entry.getStoreNameSnapshot() != null ? entry.getStoreNameSnapshot() : "no store",
                    entry.getActivity());
        } catch (Exception ex) {
            log.warn("Failed to persist activity log", ex);
        }
    }

    public void record(User user, String activity) {
        if (user == null) {
            return;
        }
        record(user.getId(), activity);
    }

    @Transactional(readOnly = true)
    public List<ActivityLogResponse> getLogs(int limit, Long storeId, String role, String search, LocalDate startDate, LocalDate endDate) {
        Specification<ActivityLog> spec = (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();

            if (storeId != null) {
                predicates.add(cb.equal(root.get("store").get("storeId"), storeId));
            }

            if (role != null && !role.isBlank()) {
                predicates.add(cb.equal(root.get("roleSnapshot"), role));
            }

            if (search != null && !search.isBlank()) {
                String pattern = "%" + search.toLowerCase() + "%";
                Predicate searchPredicate = cb.or(
                        cb.like(cb.lower(root.get("activity")), pattern),
                        cb.like(cb.lower(root.get("fullNameSnapshot")), pattern),
                        cb.like(cb.lower(root.get("usernameSnapshot")), pattern)
                );
                predicates.add(searchPredicate);
            }

            if (startDate != null) {
                predicates.add(cb.greaterThanOrEqualTo(root.get("logTimestamp"), startDate.atStartOfDay()));
            }

            if (endDate != null) {
                predicates.add(cb.lessThan(root.get("logTimestamp"), endDate.plusDays(1).atStartOfDay()));
            }

            return cb.and(predicates.toArray(new Predicate[0]));
        };

        PageRequest pageRequest = PageRequest.of(0, limit, Sort.by(Sort.Direction.DESC, "logTimestamp", "logId"));
        return activityLogRepository.findAll(spec, pageRequest).stream()
                .map(this::toResponse)
                .toList();
    }

    @Transactional(readOnly = true)
    public List<ActivityLogResponse> getLogs(int limit, Long storeId, String role, String search) {
        return getLogs(limit, storeId, role, search, null, null);
    }

    @Transactional(readOnly = true)
    public List<ActivityLogResponse> getLogs(int limit) {
        return getLogs(limit, null, null, null, null, null);
    }

    private ActivityLogResponse toResponse(ActivityLog entry) {
        ActivityLogResponse response = new ActivityLogResponse();
        response.setLogId(entry.getLogId());

        if (entry.getUser() != null) {
            response.setUserId(entry.getUser().getId());
            response.setUsername(firstNonBlank(entry.getUsernameSnapshot(), entry.getUser().getUsername()));
            String liveName = entry.getUser().getFullName() != null && !entry.getUser().getFullName().isBlank()
                    ? entry.getUser().getFullName()
                    : StringUtils.fullName(entry.getUser().getFirstName(), entry.getUser().getLastName());
            response.setFullName(firstNonBlank(entry.getFullNameSnapshot(), liveName));
            response.setRole(firstNonBlank(entry.getRoleSnapshot(), entry.getUser().getRole() != null ? entry.getUser().getRole().name() : null));
        }

        StoreLocation store = entry.getStore();
        if (store != null) {
            response.setStoreId(store.getStoreId());
            response.setStoreName(firstNonBlank(entry.getStoreNameSnapshot(), store.getStoreName()));
        }

        response.setUsernameSnapshot(entry.getUsernameSnapshot());
        response.setFullNameSnapshot(entry.getFullNameSnapshot());
        response.setRoleSnapshot(entry.getRoleSnapshot());
        response.setStoreNameSnapshot(entry.getStoreNameSnapshot());

        response.setActivity(entry.getActivity());
        response.setLogTimestamp(entry.getLogTimestamp());
        return response;
    }

    private String firstNonBlank(String preferred, String fallback) {
        if (preferred != null && !preferred.isBlank()) {
            return preferred;
        }
        return fallback;
    }
}
