package net.siegeln.cameleer.saas.audit;

import net.siegeln.cameleer.saas.identity.LogtoManagementClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

@Service
public class AuditService {

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

    private final AuditRepository auditRepository;
    private final LogtoManagementClient logtoClient;
    private final ConcurrentHashMap<String, String> userNameCache = new ConcurrentHashMap<>();

    public AuditService(AuditRepository auditRepository, LogtoManagementClient logtoClient) {
        this.auditRepository = auditRepository;
        this.logtoClient = logtoClient;
    }

    public void log(UUID actorId, String actorEmail, UUID tenantId,
                    AuditAction action, String resource,
                    String environment, String sourceIp,
                    String result, Map<String, Object> metadata) {
        if (actorEmail == null && actorId != null) {
            actorEmail = resolveActorName(actorId.toString());
        }
        var entry = new AuditEntity();
        entry.setActorId(actorId);
        entry.setActorEmail(actorEmail);
        entry.setTenantId(tenantId);
        entry.setAction(action.name());
        entry.setResource(resource);
        entry.setEnvironment(environment);
        entry.setSourceIp(sourceIp);
        entry.setResult(result != null ? result : "SUCCESS");
        entry.setMetadata(metadata);
        auditRepository.save(entry);
    }

    public Page<AuditDto.AuditLogEntry> search(UUID tenantId, String action, String result,
                                               Instant from, Instant to, String search,
                                               Pageable pageable) {
        return auditRepository.findFiltered(tenantId, action, result, from, to, search, pageable);
    }

    private String resolveActorName(String userId) {
        return userNameCache.computeIfAbsent(userId, id -> {
            try {
                var user = logtoClient.getUser(id);
                if (user == null) return id;
                var username = user.get("username");
                if (username != null && !username.toString().isBlank()) return username.toString();
                var name = user.get("name");
                if (name != null && !name.toString().isBlank()) return name.toString();
                var email = user.get("primaryEmail");
                if (email != null && !email.toString().isBlank()) return email.toString();
                return id;
            } catch (Exception e) {
                log.warn("Failed to resolve actor name for {}: {}", id, e.getMessage());
                return id;
            }
        });
    }
}
