package de.gecheckt.pdf.umbenenner.application.port.in;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.Test;

/**
 * Unit tests for {@link BatchRunOutcome} enumeration.
 * <p>
 * Verifies that all required outcome values are present and correctly defined,
 * and that the business logic methods work as expected.
 */
class BatchRunOutcomeTest {

    @Test
    void allRequiredOutcomeValuesExist() {
        // Verify all outcome values required by the architecture are present
        assertNotNull(BatchRunOutcome.SUCCESS);
        assertNotNull(BatchRunOutcome.LOCK_UNAVAILABLE);
        assertNotNull(BatchRunOutcome.FAILURE);
    }

    @Test
    void successOutcome_hasCorrectMessage() {
        BatchRunOutcome outcome = BatchRunOutcome.SUCCESS;
        assertEquals("Batch processing completed successfully", outcome.message());
    }

    @Test
    void lockUnavailableOutcome_hasCorrectMessage() {
        BatchRunOutcome outcome = BatchRunOutcome.LOCK_UNAVAILABLE;
        assertEquals("Another instance is already running; this run terminates immediately", outcome.message());
    }

    @Test
    void failureOutcome_hasCorrectMessage() {
        BatchRunOutcome outcome = BatchRunOutcome.FAILURE;
        assertEquals("Batch processing failed", outcome.message());
    }

    @Test
    void isSuccess_returnsTrueOnlyForSuccess() {
        assertTrue(BatchRunOutcome.SUCCESS.isSuccess());
        assertFalse(BatchRunOutcome.LOCK_UNAVAILABLE.isSuccess());
        assertFalse(BatchRunOutcome.FAILURE.isSuccess());
    }

    @Test
    void isFailure_returnsTrueForFailureAndLockUnavailable() {
        assertFalse(BatchRunOutcome.SUCCESS.isFailure());
        assertTrue(BatchRunOutcome.LOCK_UNAVAILABLE.isFailure());
        assertTrue(BatchRunOutcome.FAILURE.isFailure());
    }

    @Test
    void isLockUnavailable_returnsTrueOnlyForLockUnavailable() {
        assertFalse(BatchRunOutcome.SUCCESS.isLockUnavailable());
        assertTrue(BatchRunOutcome.LOCK_UNAVAILABLE.isLockUnavailable());
        assertFalse(BatchRunOutcome.FAILURE.isLockUnavailable());
    }

    @Test
    void outcomeEquality_worksByReference() {
        // Enums have identity-based equality
        assertSame(BatchRunOutcome.SUCCESS, BatchRunOutcome.SUCCESS);
        assertNotSame(BatchRunOutcome.SUCCESS, BatchRunOutcome.FAILURE);
    }

    @Test
    void outcomeValues_areThreeInTotal() {
        BatchRunOutcome[] values = BatchRunOutcome.values();
        assertEquals(3, values.length, "BatchRunOutcome should have exactly 3 values");
    }

    @Test
    void outcomeValues_haveCorrectOrder() {
        BatchRunOutcome[] values = BatchRunOutcome.values();
        assertSame(BatchRunOutcome.SUCCESS, values[0]);
        assertSame(BatchRunOutcome.LOCK_UNAVAILABLE, values[1]);
        assertSame(BatchRunOutcome.FAILURE, values[2]);
    }

    @Test
    void outcomeCanBeUsedInSwitch() {
        BatchRunOutcome outcome = BatchRunOutcome.LOCK_UNAVAILABLE;
        String result = "";

        switch (outcome) {
            case SUCCESS -> result = "success";
            case LOCK_UNAVAILABLE -> result = "lock-unavailable";
            case FAILURE -> result = "failure";
        }

        assertEquals("lock-unavailable", result);
    }

    @Test
    void toString_returnsName() {
        assertEquals("SUCCESS", BatchRunOutcome.SUCCESS.toString());
        assertEquals("LOCK_UNAVAILABLE", BatchRunOutcome.LOCK_UNAVAILABLE.toString());
        assertEquals("FAILURE", BatchRunOutcome.FAILURE.toString());
    }

    @Test
    void enumHashCode_consistency() {
        // Test hashCode consistency for enum values
        assertEquals(BatchRunOutcome.SUCCESS.hashCode(), BatchRunOutcome.SUCCESS.hashCode());
        assertNotEquals(BatchRunOutcome.SUCCESS.hashCode(), BatchRunOutcome.FAILURE.hashCode());
    }

    @Test
    void enumEquals_consistency() {
        // Test equals consistency for enum values
        assertEquals(BatchRunOutcome.SUCCESS, BatchRunOutcome.SUCCESS);
        assertNotEquals(BatchRunOutcome.SUCCESS, BatchRunOutcome.FAILURE);
        // Enums should not equal null
        assertNotEquals(BatchRunOutcome.SUCCESS, null);
        // Enums should not equal objects of different types
        assertNotEquals(BatchRunOutcome.SUCCESS, "SUCCESS");
    }

    @Test
    void allMethods_coveredInTests() {
        // Ensure all public methods are tested at least once with each enum value
        for (BatchRunOutcome outcome : BatchRunOutcome.values()) {
            // Just invoke each method to ensure no runtime exceptions
            assertNotNull(outcome.message());
            outcome.isSuccess();
            outcome.isFailure();
            outcome.isLockUnavailable();
            assertNotNull(outcome.toString());
            assertEquals(outcome.hashCode(), outcome.hashCode()); // Test consistency
        }
    }
}