#!/usr/bin/env python3
"""
Exhaustive string conversion tests for type executors.
"""

from __future__ import annotations

import pytest

from exonware.xwquery.contracts import ExecutionContext, QueryAction
from exonware.xwquery.runtime.executors import get_operation_registry


def _run(op: str, data: list[dict], params: dict, out_field: str = "out") -> list:
    executor = get_operation_registry().get(op)
    assert executor is not None, f"Executor not found: {op}"
    result = executor._do_execute(QueryAction(type=op, params=params), ExecutionContext(node=data, metadata={}))
    assert result.success, f"{op} execution failed: {result.error}"
    return [row.get(out_field) for row in result.data["items"]]


@pytest.mark.xwquery_core
def test_to_number_handles_common_string_forms():
    data = [
        {"v": "42"},
        {"v": "3.14"},
        {"v": "  1,234.50 "},
        {"v": "1_000"},
        {"v": "$99.9"},
        {"v": "(123.45)"},
        {"v": "0x10"},
        {"v": "0b1010"},
        {"v": "0o12"},
        {"v": "abc"},
        {"v": None},
    ]

    got_float = _run("TO_NUMBER", data, {"field": "v", "number_type": "float", "as_field": "out"})
    assert got_float[:9] == [42.0, 3.14, 1234.5, 1000.0, 99.9, -123.45, 16.0, 10.0, 10.0]
    assert got_float[9:] == [None, None]

    got_int = _run("TO_NUMBER", data, {"field": "v", "number_type": "int", "as_field": "out"})
    assert got_int[:9] == [42, 3, 1234, 1000, 99, -123, 16, 10, 10]
    assert got_int[9:] == [None, None]


@pytest.mark.xwquery_core
def test_to_boolean_strict_and_lenient_modes():
    data = [
        {"b": "true"},
        {"b": "FALSE"},
        {"b": " yes "},
        {"b": "off"},
        {"b": "1"},
        {"b": "0"},
        {"b": "maybe"},
        {"b": ""},
        {"b": None},
        {"b": 2},
        {"b": 0},
    ]

    strict_vals = _run("TO_BOOLEAN", data, {"field": "b", "strict": True, "default": None, "as_field": "out"})
    assert strict_vals == [True, False, True, False, True, False, None, None, None, True, False]

    lenient_vals = _run("TO_BOOLEAN", data, {"field": "b", "strict": False, "default": None, "as_field": "out"})
    assert lenient_vals[0:6] == [True, False, True, False, True, False]
    assert lenient_vals[6] is True  # maybe -> truthy in lenient mode


@pytest.mark.xwquery_core
def test_to_date_accepts_multiple_string_formats_and_epoch():
    data = [
        {"d": "2025-01-31"},
        {"d": "2025/01/31"},
        {"d": "31/01/2025"},
        {"d": "01/31/2025"},
        {"d": "2025-01-31 12:34:56"},
        {"d": "2025-01-31T12:34:56"},
        {"d": "2025-01-31T12:34:56Z"},
        {"d": 1738281600},  # epoch seconds (2025-01-31 UTC)
        {"d": 1738281600000},  # epoch ms (2025-01-31 UTC)
        {"d": "invalid"},
        {"d": None},
    ]

    vals = _run("TO_DATE", data, {"field": "d", "as_field": "out"})
    assert vals[:9] == ["2025-01-31"] * 9
    assert vals[9:] == [None, None]


@pytest.mark.xwquery_core
def test_cast_and_try_cast_from_strings():
    data = [
        {"v": "42"},
        {"v": "false"},
        {"v": "2025-01-31"},
        {"v": "abc"},
    ]

    cast_int = _run("CAST", data, {"field": "v", "target_type": "int", "as_field": "out"})
    assert cast_int == [42, None, None, None]

    cast_bool = _run("CAST", data, {"field": "v", "target_type": "bool", "as_field": "out"})
    assert cast_bool == [None, False, None, None]

    cast_date = _run("CAST", data, {"field": "v", "target_type": "date", "as_field": "out"})
    assert cast_date == [None, None, "2025-01-31", None]

    try_cast_int = _run(
        "TRY_CAST",
        data,
        {"field": "v", "target_type": "int", "default": "ERR", "as_field": "out"},
    )
    assert try_cast_int == [42, "ERR", "ERR", "ERR"]

