package de.gecheckt.pdf.umbenenner.domain.model;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.Optional;

import org.junit.jupiter.api.Test;

/**
 * Tests for AI-related domain types:
 * {@link ParsedAiResponse}, {@link AiRequestRepresentation},
 * {@link AiResponseParsingFailure}, {@link AiResponseParsingSuccess},
 * and {@link AiErrorClassification}.
 */
class AiResponseTypesTest {

    // -------------------------------------------------------------------------
    // ParsedAiResponse
    // -------------------------------------------------------------------------

    @Test
    void parsedAiResponse_withDate_fieldsAreSet() {
        var parsed = new ParsedAiResponse("Rechnung", "AI reasoning", Optional.of("2026-01-15"));

        assertEquals("Rechnung", parsed.title());
        assertEquals("AI reasoning", parsed.reasoning());
        assertTrue(parsed.dateString().isPresent());
        assertEquals("2026-01-15", parsed.dateString().get());
    }

    @Test
    void parsedAiResponse_withoutDate_dateStringIsEmpty() {
        var parsed = new ParsedAiResponse("Rechnung", "AI reasoning", Optional.empty());

        assertFalse(parsed.dateString().isPresent());
    }

    @Test
    void parsedAiResponse_factoryMethod_withDate_wrapsDate() {
        var parsed = ParsedAiResponse.of("Vertrag", "reasoning", "2026-03-01");

        assertEquals("Vertrag", parsed.title());
        assertTrue(parsed.dateString().isPresent());
        assertEquals("2026-03-01", parsed.dateString().get());
    }

    @Test
    void parsedAiResponse_factoryMethod_withNullDate_producesEmpty() {
        var parsed = ParsedAiResponse.of("Vertrag", "reasoning", null);

        assertFalse(parsed.dateString().isPresent());
    }

    @Test
    void parsedAiResponse_nullTitle_throwsNPE() {
        assertThrows(NullPointerException.class,
                () -> new ParsedAiResponse(null, "reasoning", Optional.empty()));
    }

    @Test
    void parsedAiResponse_nullReasoning_throwsNPE() {
        assertThrows(NullPointerException.class,
                () -> new ParsedAiResponse("title", null, Optional.empty()));
    }

    @Test
    void parsedAiResponse_nullDateString_throwsNPE() {
        assertThrows(NullPointerException.class,
                () -> new ParsedAiResponse("title", "reasoning", null));
    }

    // -------------------------------------------------------------------------
    // AiRequestRepresentation
    // -------------------------------------------------------------------------

    @Test
    void aiRequestRepresentation_validConstruction_fieldsAreSet() {
        var promptId = new PromptIdentifier("prompt-v1");
        var repr = new AiRequestRepresentation(promptId, "Prompt text", "Document content", 16);

        assertEquals(promptId, repr.promptIdentifier());
        assertEquals("Prompt text", repr.promptContent());
        assertEquals("Document content", repr.documentText());
        assertEquals(16, repr.sentCharacterCount());
    }

    @Test
    void aiRequestRepresentation_zeroSentCharacterCount_isValid() {
        var promptId = new PromptIdentifier("p");
        var repr = new AiRequestRepresentation(promptId, "prompt", "text", 0);
        assertEquals(0, repr.sentCharacterCount());
    }

    @Test
    void aiRequestRepresentation_sentCharCountEqualsTextLength_isValid() {
        var promptId = new PromptIdentifier("p");
        String text = "hello";
        var repr = new AiRequestRepresentation(promptId, "prompt", text, text.length());
        assertEquals(text.length(), repr.sentCharacterCount());
    }

    @Test
    void aiRequestRepresentation_negativeSentCharCount_throwsIllegalArgument() {
        var promptId = new PromptIdentifier("p");
        assertThrows(IllegalArgumentException.class,
                () -> new AiRequestRepresentation(promptId, "prompt", "text", -1));
    }

    @Test
    void aiRequestRepresentation_sentCharCountExceedsTextLength_throwsIllegalArgument() {
        var promptId = new PromptIdentifier("p");
        assertThrows(IllegalArgumentException.class,
                () -> new AiRequestRepresentation(promptId, "prompt", "short", 100));
    }

    @Test
    void aiRequestRepresentation_nullPromptIdentifier_throwsNPE() {
        assertThrows(NullPointerException.class,
                () -> new AiRequestRepresentation(null, "prompt", "text", 4));
    }

    @Test
    void aiRequestRepresentation_nullPromptContent_throwsNPE() {
        var promptId = new PromptIdentifier("p");
        assertThrows(NullPointerException.class,
                () -> new AiRequestRepresentation(promptId, null, "text", 4));
    }

    @Test
    void aiRequestRepresentation_nullDocumentText_throwsNPE() {
        var promptId = new PromptIdentifier("p");
        assertThrows(NullPointerException.class,
                () -> new AiRequestRepresentation(promptId, "prompt", null, 0));
    }

    // -------------------------------------------------------------------------
    // AiResponseParsingFailure
    // -------------------------------------------------------------------------

    @Test
    void aiResponseParsingFailure_fieldsAreSet() {
        var failure = new AiResponseParsingFailure("MISSING_TITLE", "AI response missing mandatory field 'title'");

        assertEquals("MISSING_TITLE", failure.failureReason());
        assertEquals("AI response missing mandatory field 'title'", failure.failureMessage());
        assertInstanceOf(AiResponseParsingResult.class, failure);
    }

    @Test
    void aiResponseParsingFailure_nullFailureReason_throwsNPE() {
        assertThrows(NullPointerException.class,
                () -> new AiResponseParsingFailure(null, "message"));
    }

    @Test
    void aiResponseParsingFailure_nullFailureMessage_throwsNPE() {
        assertThrows(NullPointerException.class,
                () -> new AiResponseParsingFailure("REASON", null));
    }

    // -------------------------------------------------------------------------
    // AiResponseParsingSuccess
    // -------------------------------------------------------------------------

    @Test
    void aiResponseParsingSuccess_fieldsAreSet() {
        var parsed = ParsedAiResponse.of("Rechnung", "reasoning", "2026-01-01");
        var success = new AiResponseParsingSuccess(parsed);

        assertEquals(parsed, success.response());
        assertInstanceOf(AiResponseParsingResult.class, success);
    }

    @Test
    void aiResponseParsingSuccess_nullResponse_throwsNPE() {
        assertThrows(NullPointerException.class,
                () -> new AiResponseParsingSuccess(null));
    }

    // -------------------------------------------------------------------------
    // AiErrorClassification
    // -------------------------------------------------------------------------

    @Test
    void aiErrorClassification_hasTwoValues() {
        AiErrorClassification[] values = AiErrorClassification.values();
        assertEquals(2, values.length);
    }

    @Test
    void aiErrorClassification_technical_isEnumValue() {
        assertEquals(AiErrorClassification.TECHNICAL,
                AiErrorClassification.valueOf("TECHNICAL"));
    }

    @Test
    void aiErrorClassification_functional_isEnumValue() {
        assertEquals(AiErrorClassification.FUNCTIONAL,
                AiErrorClassification.valueOf("FUNCTIONAL"));
    }
}
