package com.larscheidschmitzhermes.keycloak.events;

import java.io.File;
import java.io.IOException;

import org.apache.commons.io.FileUtils;
import org.hamcrest.MatcherAssert;
import org.hamcrest.core.Is;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.keycloak.events.Event;
import org.keycloak.events.EventType;
import org.keycloak.events.admin.AdminEvent;
import org.keycloak.events.admin.AuthDetails;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;

public class MonitoringEventListenerProviderTest {
    @Rule
    public TemporaryFolder tmp = new TemporaryFolder();

    private Event event() {
        Event event = new Event();
            event.setClientId("23");
            event.setIpAddress("4.4.4.4");
            event.setRealmId("test-realm");
            event.setType(EventType.LOGIN);
            return event;
    }

    private Event maliciousEvent() {
        Event event = new Event();
            event.setClientId("a'a\\'b\"c>?>%}}%%>c<[[?${{%}}cake\\");
            event.setIpAddress("4.4.4.4");
            event.setRealmId("test-realm");
            event.setType(EventType.LOGIN);
            return event;
    }

    private AdminEvent adminEvent() {
        AuthDetails details = new AuthDetails();
        details.setClientId("42");
        details.setIpAddress("1.2.3.4");
        AdminEvent event = new AdminEvent();
            event.setRealmId("test-realm");
            event.setAuthDetails(details);
            event.setOperationType(OperationType.UPDATE);
            event.setResourceType(ResourceType.CLIENT);
            return event;
    }

    @Test
    public void shouldGenerateFilesWithCorrectNamesForNormalEvents() throws IOException {
        MonitoringEventListenerProvider listener = new MonitoringEventListenerProvider(tmp.getRoot().getAbsolutePath());

        listener.onEvent(event());

        File expectedFile = new File(tmp.getRoot().getAbsolutePath() + File.separator + "keycloak_events_total;realm=test-realm;client_id=23;ip_address=4.4.4.4;type=LOGIN");

        MatcherAssert.assertThat(expectedFile.exists(), Is.is(true));
        MatcherAssert.assertThat(FileUtils.readFileToString(expectedFile), Is.is("1"));
    }

    @Test
    public void shouldGenerateFilesWithCorrectNamesForMaliciousEvents() throws IOException {
        MonitoringEventListenerProvider listener = new MonitoringEventListenerProvider(tmp.getRoot().getAbsolutePath());

        listener.onEvent(maliciousEvent());

        File expectedFile = new File(tmp.getRoot().getAbsolutePath() + File.separator + "keycloak_events_total;realm=test-realm;client_id=a_a__b_c_________c__________cake_;ip_address=4.4.4.4;type=LOGIN");

        MatcherAssert.assertThat(expectedFile.exists(), Is.is(true));
        MatcherAssert.assertThat(FileUtils.readFileToString(expectedFile), Is.is("1"));
    }

    @Test
    public void shouldGenerateFilesWithCorrectNamesForAdminEvents() throws IOException {
        MonitoringEventListenerProvider listener = new MonitoringEventListenerProvider(tmp.getRoot().getAbsolutePath());

        listener.onEvent(adminEvent(), false);

        File expectedFile = new File(tmp.getRoot().getAbsolutePath() + File.separator + "keycloak_admin_events_total;realm=test-realm;client_id=42;ip_address=1.2.3.4;operation=UPDATE;resource=CLIENT");

        MatcherAssert.assertThat(expectedFile.exists(), Is.is(true));
        MatcherAssert.assertThat(FileUtils.readFileToString(expectedFile), Is.is("1"));
    }

    @Test
    public void shouldProperlyIncreaseCounterForNormalEvents() throws IOException {
        MonitoringEventListenerProvider listener = new MonitoringEventListenerProvider(tmp.getRoot().getAbsolutePath());
        File existingCounter = new File(tmp.getRoot().getAbsolutePath() + File.separator + "keycloak_events_total;realm=test-realm;client_id=23;ip_address=4.4.4.4;type=LOGIN");
        FileUtils.writeStringToFile(existingCounter, "100");

        listener.onEvent(event());

        MatcherAssert.assertThat(FileUtils.readFileToString(existingCounter), Is.is("101"));
    }

    @Test
    public void shouldProperlyIncreaseCounterForAdminEvents() throws IOException {
        MonitoringEventListenerProvider listener = new MonitoringEventListenerProvider(tmp.getRoot().getAbsolutePath());
        File existingCounter = new File(tmp.getRoot().getAbsolutePath() + File.separator + "keycloak_admin_events_total;realm=test-realm;client_id=42;ip_address=1.2.3.4;operation=UPDATE;resource=CLIENT");
        FileUtils.writeStringToFile(existingCounter, "100");

        listener.onEvent(adminEvent(), false);

        MatcherAssert.assertThat(FileUtils.readFileToString(existingCounter), Is.is("101"));
    }

    @Test(expected = RuntimeException.class)
    public void shouldPropagateAnyFileRelatedErrors() {
        MonitoringEventListenerProvider listener = new MonitoringEventListenerProvider(tmp.getRoot().getAbsolutePath() + File.separator + "non-existent");

        listener.onEvent(event());
    }
}