package com.atlassian.bitbucket.mesh.repair;

import com.atlassian.bitbucket.mesh.junit.LogListener;
import org.junit.Rule;
import org.junit.Test;

import java.time.Duration;
import java.time.Instant;

import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;

public class RepairGateTest {

    @Rule
    public final LogListener logListener = new LogListener(RepairGate.class);

    @Test
    public void testConcurrencyEnforced() {
        RepairGate gate = new RepairGate(2);

        RepairTicket ticket1 = gate.acquireTicket("repo1", Duration.ZERO);
        assertEquals(1, gate.getActiveCount());
        RepairTicket ticket2 = gate.acquireTicket("repo2", Duration.ZERO);
        assertEquals(2, gate.getActiveCount());

        //Verify that the gate blocks until a slot becomes available
        Instant start = Instant.now();
        assertThrows(RepositoryRepairSlotsExhaustedException.class,
                () -> gate.acquireTicket("repo3", Duration.ofMillis(100L)));
        logListener.assertWarn("Failed to claim repair slot for repo3");
        Duration duration = Duration.between(start, Instant.now());
        assertThat(duration.toMillis(), both(greaterThanOrEqualTo(100L)).and(lessThan(250L)));
        assertEquals(2, gate.getActiveCount());

        //Return a ticket
        ticket2.close();
        assertEquals(1, gate.getActiveCount());
        RepairTicket ticket3 = gate.acquireTicket("repo3", Duration.ZERO);
        assertEquals(2, gate.getActiveCount());

        ticket1.close();
        assertEquals(1, gate.getActiveCount());
        ticket3.close();
        assertEquals(0, gate.getActiveCount());
    }

    @Test
    public void testSingleTicketPerRepositoryEnforced() {
        RepairGate gate = new RepairGate(2);

        RepairTicket ticket = gate.acquireTicket("repo", Duration.ZERO);
        assertThrows(RepositoryRepairAlreadyRunningException.class, () -> gate.acquireTicket("repo", Duration.ZERO));
        assertThrows(RepositoryRepairAlreadyRunningException.class, () -> gate.acquireTicket("repo?q=abc", Duration.ZERO));
    }

    @Test
    public void testTicketCanBeClosedOnce() {
        RepairGate gate = new RepairGate(2);

        RepairTicket ticket1 = gate.acquireTicket("repo1", Duration.ZERO);
        assertEquals(1, gate.getActiveCount());
        RepairTicket ticket2 = gate.acquireTicket("repo2", Duration.ZERO);
        assertEquals(2, gate.getActiveCount());

        ticket1.close();
        assertEquals(1, gate.getActiveCount());
        ticket1.close();
        assertEquals(1, gate.getActiveCount());

        RepairTicket ticket1b = gate.acquireTicket("repo1", Duration.ZERO);
        assertEquals(2, gate.getActiveCount());
        ticket1.close();
        assertEquals(2, gate.getActiveCount());
    }
}