from __future__ import annotations

import enum
from dataclasses import fields

import pytest

import ibm3270
from ibm3270 import (
    ConnectionState,
    EmulatorMode,
    FieldDefinition,
    FieldProtection,
    KeyboardState,
    ScreenFormatting,
    ScreenPosition,
    ScreenSize,
    StatusFlag,
    StatusInfo,
    TerminalMode,
    TerminalOptions,
    TerminalResponse,
    TerminalSetting,
    field,
)


def test_all_types_importable() -> None:
    assert TerminalOptions.__name__ == "TerminalOptions"
    assert TerminalResponse.__name__ == "TerminalResponse"
    assert ScreenPosition.__name__ == "ScreenPosition"
    assert ScreenSize.__name__ == "ScreenSize"
    assert StatusInfo.__name__ == "StatusInfo"
    assert FieldDefinition.__name__ == "FieldDefinition"


def test_terminal_options_defaults() -> None:
    opts = TerminalOptions()

    assert opts.executable == "s3270"
    assert opts.args == []
    assert opts.verbose is False
    assert opts.timeout == 30_000


def test_terminal_options_custom() -> None:
    opts = TerminalOptions(executable="/usr/local/bin/s3270", timeout=5_000)

    assert opts.executable == "/usr/local/bin/s3270"
    assert opts.timeout == 5_000


@pytest.mark.parametrize("member", ["P", "S", "N", "L", "B"])
def test_terminal_mode_members(member: str) -> None:
    assert TerminalMode(member).value == member


@pytest.mark.parametrize(
    ("enum_cls", "expected"),
    [
        (
            TerminalSetting,
            [
                "ConnectionState",
                "Host",
                "LuName",
                "Model",
                "Encoding",
                "CodePage",
                "Aid",
                "BindPluName",
            ],
        ),
        (StatusFlag, ["Formatted", "KeyboardLock", "Printer", "Secure", "Tn3270e"]),
        (KeyboardState, ["U", "L", "E"]),
        (ScreenFormatting, ["F", "U"]),
        (FieldProtection, ["P", "U"]),
        (ConnectionState, ["C", "N"]),
        (EmulatorMode, ["I", "L", "C", "P", "N"]),
    ],
)
def test_enum_members(enum_cls: type[enum.Enum], expected: list[str]) -> None:
    values = [member.value for member in enum_cls]

    assert values == expected


def test_keyboard_state_matches_string_value() -> None:
    assert KeyboardState.Unlocked == "U"


def test_screen_dataclasses() -> None:
    position = ScreenPosition(row=4, col=9)
    size = ScreenSize(rows=24, cols=80)

    assert position.row == 4
    assert position.col == 9
    assert size.rows == 24
    assert size.cols == 80


def test_terminal_response_instantiation() -> None:
    response = TerminalResponse(ok=True, data="screen", status="status", raw=["screen", "status", "ok"])

    assert response.ok is True
    assert response.data == "screen"
    assert response.status == "status"
    assert response.raw == ["screen", "status", "ok"]


def test_status_info_field_shape() -> None:
    status_fields = [field.name for field in fields(StatusInfo)]

    assert status_fields == [
        "keyboard_state",
        "screen_formatting",
        "field_protection",
        "connection_state",
        "host",
        "emulator_mode",
        "model_number",
        "rows",
        "cols",
        "cursor_row",
        "cursor_col",
        "window_id",
        "command_execution_time",
    ]


def test_status_info_instantiation() -> None:
    status = StatusInfo(
        keyboard_state=KeyboardState.Unlocked,
        screen_formatting=ScreenFormatting.Formatted,
        field_protection=FieldProtection.Unprotected,
        connection_state=ConnectionState.Connected,
        host="mvshost",
        emulator_mode=EmulatorMode.NVTCharacter,
        model_number=2,
        rows=24,
        cols=80,
        cursor_row=1,
        cursor_col=1,
        window_id="0x0",
        command_execution_time=0.042,
    )

    assert status.rows == 24
    assert status.host == "mvshost"
    assert status.command_execution_time == pytest.approx(0.042)


def test_status_info_null_exec_time() -> None:
    status = StatusInfo(
        keyboard_state=KeyboardState.Unlocked,
        screen_formatting=ScreenFormatting.Unformatted,
        field_protection=FieldProtection.Unprotected,
        connection_state=ConnectionState.NotConnected,
        host=None,
        emulator_mode=EmulatorMode.NotConnected,
        model_number=2,
        rows=24,
        cols=80,
        cursor_row=0,
        cursor_col=0,
        window_id="0x0",
        command_execution_time=None,
    )

    assert status.command_execution_time is None
    assert status.host is None


def test_field_definition_trim_default() -> None:
    field_definition = FieldDefinition(row=3, col=5, length=10)

    assert field_definition.type == "string"
    assert field_definition.trim is True
    assert field_definition.name is None


def test_field_definition_number_type() -> None:
    field_definition = FieldDefinition(
        row=1,
        col=1,
        length=5,
        type="number",
        trim=False,
        name="amount",
    )

    assert field_definition.type == "number"
    assert field_definition.trim is False
    assert field_definition.name == "amount"


def test_field_helper_defaults() -> None:
    field_definition = field(2, 12, 8)

    assert field_definition == FieldDefinition(row=2, col=12, length=8)


def test_field_helper_supports_named_number_fields() -> None:
    field_definition = field(5, 20, 10, "number", trim=False, name="balance")

    assert field_definition == FieldDefinition(
        row=5,
        col=20,
        length=10,
        type="number",
        trim=False,
        name="balance",
    )


def test_top_level_exports() -> None:
    assert ibm3270.TerminalMode is TerminalMode
    assert ibm3270.TerminalOptions is TerminalOptions
    assert ibm3270.StatusInfo is StatusInfo
    assert ibm3270.field is field


def test_version() -> None:
    assert isinstance(ibm3270.__version__, str)
    assert ibm3270.__version__.split(".") == ["0", "1", "0"]
