package com.cameleer.server.app.license;

import com.cameleer.server.core.admin.AuditCategory;
import com.cameleer.server.core.admin.AuditResult;
import com.cameleer.server.core.admin.AuditService;
import com.cameleer.server.core.license.LicenseGate;
import com.cameleer.server.core.license.LicenseInfo;
import com.cameleer.server.core.license.LicenseState;
import com.cameleer.server.core.license.LicenseValidator;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationEventPublisher;

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

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

class LicenseServiceTest {

    LicenseRepository repo;
    LicenseGate gate;
    AuditService audit;
    ApplicationEventPublisher events;
    LicenseValidator validator;
    LicenseService svc;

    @BeforeEach
    void setUp() {
        repo = mock(LicenseRepository.class);
        gate = new LicenseGate();
        audit = mock(AuditService.class);
        events = mock(ApplicationEventPublisher.class);
        validator = mock(LicenseValidator.class);
        svc = new LicenseService("default", repo, gate, validator, audit, events);
    }

    @Test
    void install_validToken_persistsAndPublishes() {
        LicenseInfo info = new LicenseInfo(UUID.randomUUID(), "default", null,
                Map.of("max_apps", 5), Instant.now(),
                Instant.now().plusSeconds(86400), 0);
        when(validator.validate("tok")).thenReturn(info);
        when(repo.findByTenantId("default")).thenReturn(Optional.empty());

        svc.install("tok", "alice", "api");

        assertThat(gate.getCurrent()).isEqualTo(info);
        assertThat(gate.getState()).isEqualTo(LicenseState.ACTIVE);
        verify(repo).upsert(any(LicenseRecord.class));
        verify(events).publishEvent(any(LicenseChangedEvent.class));
        verify(audit).log(eq("alice"), eq("install_license"), eq(AuditCategory.LICENSE),
                any(), any(), eq(AuditResult.SUCCESS), isNull());
    }

    @Test
    void install_invalidToken_marksGateInvalidAndAudits() {
        when(validator.validate("bad")).thenThrow(new SecurityException("signature failed"));

        try {
            svc.install("bad", "alice", "api");
        } catch (Exception ignored) {}

        assertThat(gate.getState()).isEqualTo(LicenseState.INVALID);
        assertThat(gate.getInvalidReason()).contains("signature failed");
        verify(repo, never()).upsert(any());
        verify(audit).log(eq("alice"), eq("reject_license"), eq(AuditCategory.LICENSE),
                any(), any(), eq(AuditResult.FAILURE), isNull());
    }

    @Test
    void install_replacingExistingLicense_auditsReplace() {
        LicenseInfo old = new LicenseInfo(UUID.randomUUID(), "default", null,
                Map.of(), Instant.now(),
                Instant.now().plusSeconds(86400), 0);
        gate.load(old);
        when(repo.findByTenantId("default")).thenReturn(Optional.of(
                new LicenseRecord("default", "old", old.licenseId(),
                        Instant.now(), "system",
                        Instant.now().plusSeconds(86400), Instant.now())));
        LicenseInfo fresh = new LicenseInfo(UUID.randomUUID(), "default", null,
                Map.of(), Instant.now(),
                Instant.now().plusSeconds(86400), 0);
        when(validator.validate("new")).thenReturn(fresh);

        svc.install("new", "alice", "api");

        verify(audit).log(eq("alice"), eq("replace_license"), eq(AuditCategory.LICENSE),
                any(), any(), eq(AuditResult.SUCCESS), isNull());
    }
}
