package de.gecheckt.pdf.umbenenner.adapter.in.gui.editor;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatNullPointerException;

import org.junit.jupiter.api.Test;

import de.gecheckt.pdf.umbenenner.application.config.provider.AiProviderFamily;

/**
 * Tests for {@link GuiVisibleProviderSection}.
 */
class GuiVisibleProviderSectionTest {

    private static final GuiProviderConfigurationState CLAUDE_STATE =
            new GuiProviderConfigurationState("https://api.anthropic.com", "claude-3-5-sonnet", "30",
                    GuiProviderApiKeyState.unresolved("claude-key"));

    private static final GuiProviderConfigurationState OPENAI_STATE =
            new GuiProviderConfigurationState("https://api.openai.com", "gpt-4o", "60",
                    GuiProviderApiKeyState.unresolved("openai-key"));

    @Test
    void storesAllFields() {
        var section = new GuiVisibleProviderSection(
                AiProviderFamily.CLAUDE, CLAUDE_STATE,
                AiProviderFamily.OPENAI_COMPATIBLE, OPENAI_STATE);

        assertThat(section.visibleProvider()).isEqualTo(AiProviderFamily.CLAUDE);
        assertThat(section.visibleProviderState()).isEqualTo(CLAUDE_STATE);
        assertThat(section.hiddenProvider()).isEqualTo(AiProviderFamily.OPENAI_COMPATIBLE);
        assertThat(section.hiddenProviderState()).isEqualTo(OPENAI_STATE);
    }

    @Test
    void stateFor_returnsVisibleProviderState() {
        var section = new GuiVisibleProviderSection(
                AiProviderFamily.CLAUDE, CLAUDE_STATE,
                AiProviderFamily.OPENAI_COMPATIBLE, OPENAI_STATE);

        assertThat(section.stateFor(AiProviderFamily.CLAUDE)).isEqualTo(CLAUDE_STATE);
    }

    @Test
    void stateFor_returnsHiddenProviderState() {
        var section = new GuiVisibleProviderSection(
                AiProviderFamily.CLAUDE, CLAUDE_STATE,
                AiProviderFamily.OPENAI_COMPATIBLE, OPENAI_STATE);

        assertThat(section.stateFor(AiProviderFamily.OPENAI_COMPATIBLE)).isEqualTo(OPENAI_STATE);
    }

    @Test
    void switchProvider_swapsVisibleAndHiddenWithoutLosingValues() {
        var section = new GuiVisibleProviderSection(
                AiProviderFamily.CLAUDE, CLAUDE_STATE,
                AiProviderFamily.OPENAI_COMPATIBLE, OPENAI_STATE);

        var switched = section.switchProvider();

        assertThat(switched.visibleProvider()).isEqualTo(AiProviderFamily.OPENAI_COMPATIBLE);
        assertThat(switched.visibleProviderState()).isEqualTo(OPENAI_STATE);
        assertThat(switched.hiddenProvider()).isEqualTo(AiProviderFamily.CLAUDE);
        assertThat(switched.hiddenProviderState()).isEqualTo(CLAUDE_STATE);
    }

    @Test
    void withVisibleProviderState_replacesOnlyVisibleState() {
        var section = new GuiVisibleProviderSection(
                AiProviderFamily.CLAUDE, CLAUDE_STATE,
                AiProviderFamily.OPENAI_COMPATIBLE, OPENAI_STATE);

        var updatedState = GuiProviderConfigurationState.blank();
        var updated = section.withVisibleProviderState(updatedState);

        assertThat(updated.visibleProviderState()).isEqualTo(updatedState);
        assertThat(updated.hiddenProviderState()).isEqualTo(OPENAI_STATE); // unchanged
        assertThat(updated.visibleProvider()).isEqualTo(AiProviderFamily.CLAUDE);
        assertThat(updated.hiddenProvider()).isEqualTo(AiProviderFamily.OPENAI_COMPATIBLE);
    }

    @Test
    void rejectsSameVisibleAndHiddenProvider() {
        assertThatIllegalArgumentException()
                .isThrownBy(() -> new GuiVisibleProviderSection(
                        AiProviderFamily.CLAUDE, CLAUDE_STATE,
                        AiProviderFamily.CLAUDE, OPENAI_STATE));
    }

    @Test
    void rejectsNullVisibleProvider() {
        assertThatNullPointerException()
                .isThrownBy(() -> new GuiVisibleProviderSection(
                        null, CLAUDE_STATE,
                        AiProviderFamily.OPENAI_COMPATIBLE, OPENAI_STATE));
    }

    @Test
    void rejectsNullHiddenProvider() {
        assertThatNullPointerException()
                .isThrownBy(() -> new GuiVisibleProviderSection(
                        AiProviderFamily.CLAUDE, CLAUDE_STATE,
                        null, OPENAI_STATE));
    }

    @Test
    void stateFor_rejectsUnknownFamily() {
        var section = new GuiVisibleProviderSection(
                AiProviderFamily.CLAUDE, CLAUDE_STATE,
                AiProviderFamily.OPENAI_COMPATIBLE, OPENAI_STATE);

        // Use a mock/non-existing provider — since we only have 2 values and both are used,
        // we can test with a null check instead to verify the guard runs
        assertThatNullPointerException()
                .isThrownBy(() -> section.stateFor(null));
    }
}
