package net.siegeln.cameleer.saas.license;

import net.siegeln.cameleer.saas.audit.AuditAction;
import net.siegeln.cameleer.saas.audit.AuditService;
import net.siegeln.cameleer.saas.tenant.TenantEntity;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.Instant;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;

@Service
public class LicenseService {

    private final LicenseRepository licenseRepository;
    private final AuditService auditService;

    public LicenseService(LicenseRepository licenseRepository, AuditService auditService) {
        this.licenseRepository = licenseRepository;
        this.auditService = auditService;
    }

    public LicenseEntity generateLicense(TenantEntity tenant, Duration validity, UUID actorId) {
        var features = LicenseDefaults.featuresForTier(tenant.getTier());
        var limits = LicenseDefaults.limitsForTier(tenant.getTier());
        Instant now = Instant.now();
        Instant expiresAt = now.plus(validity);

        String token = UUID.randomUUID().toString();

        var entity = new LicenseEntity();
        entity.setTenantId(tenant.getId());
        entity.setTier(tenant.getTier().name());
        entity.setFeatures(features);
        entity.setLimits(limits);
        entity.setIssuedAt(now);
        entity.setExpiresAt(expiresAt);
        entity.setToken(token);

        var saved = licenseRepository.save(entity);

        auditService.log(actorId, null, tenant.getId(),
                AuditAction.LICENSE_GENERATE, saved.getId().toString(),
                null, null, "SUCCESS", null);

        return saved;
    }

    public Optional<LicenseEntity> getActiveLicense(UUID tenantId) {
        return licenseRepository.findFirstByTenantIdAndRevokedAtIsNullOrderByCreatedAtDesc(tenantId);
    }

    public void revokeLicense(UUID tenantId, UUID actorId) {
        licenseRepository.findFirstByTenantIdAndRevokedAtIsNullOrderByCreatedAtDesc(tenantId)
            .ifPresent(license -> {
                license.setRevokedAt(Instant.now());
                licenseRepository.save(license);
                auditService.log(actorId, null, tenantId,
                    AuditAction.LICENSE_REVOKE, "license",
                    null, null, null, Map.of("licenseId", license.getId().toString()));
            });
    }

    /**
     * Verifies a license token by checking its existence and validity in the database.
     * Returns the license entity's metadata as a map if found and not expired/revoked,
     * or empty if the token is unknown or invalid.
     */
    public Optional<Map<String, Object>> verifyLicenseToken(String token) {
        return licenseRepository.findByToken(token)
                .filter(e -> e.getRevokedAt() == null)
                .filter(e -> e.getExpiresAt() == null || Instant.now().isBefore(e.getExpiresAt()))
                .map(e -> Map.<String, Object>of(
                        "tenant_id", e.getTenantId().toString(),
                        "tier", e.getTier(),
                        "features", e.getFeatures(),
                        "limits", e.getLimits()
                ));
    }
}
