# SPDX-License-Identifier: MIT
# SPDX-FileCopyrightText: 2021 Taneli Hukkinen
# Licensed to PSF under a Contributor Agreement.
from typing import Any

import pytest
import toml_rs
import toml_rs as tomllib

from .helpers import _dedent


def test_line_and_col() -> None:
    # invalid mantissa
    with pytest.raises(tomllib.TOMLDecodeError) as exc:
        tomllib.loads("val=.")
    msg = str(exc.value)
    assert "line 1, column 5" in msg
    assert "invalid mantissa" in msg

    # invalid int
    with pytest.raises(tomllib.TOMLDecodeError) as exc:
        tomllib.loads("x = 0x")

    exc = exc.value

    assert exc.colno == 5
    assert exc.lineno == 1
    assert exc.pos == 4
    assert exc.doc == "x = 0x"
    assert (
        str(exc.msg) ==
        _dedent("""
        TOML parse error at line 1, column 5
          |
        1 | x = 0x
          |     ^^
        invalid integer '0x'
        """)
    )

    with pytest.raises(tomllib.TOMLDecodeError) as exc:
        tomllib.loads("a = \"\"\"\r\"\"\"")  # noqa: Q003

    assert str(exc.value.msg) == (
        _dedent('''
        TOML parse error at line 1, column 9
          |
        1 | a = """\r"""
          |         ^
        carriage return must be followed by newline, expected newline
        ''')
    )

    with pytest.raises(tomllib.TOMLDecodeError) as exc:
        tomllib.loads("a = \"\"\"\b\"\"\"")  # noqa: Q003

    assert str(exc.value.msg) == (
        _dedent('''
        TOML parse error at line 1, column 8
          |
        1 | a = """\b"""
          |        ^
        invalid multi-line basic string, expected `\\`, characters
        ''')
    )

    # missing value
    with pytest.raises(tomllib.TOMLDecodeError) as exc:
        tomllib.loads(".")
    msg = str(exc.value)
    assert "line 1, column" in msg
    assert "missing value" in msg

    # multiple newlines
    with pytest.raises(tomllib.TOMLDecodeError) as exc:
        tomllib.loads("\n\nval=.")
    msg = str(exc.value)
    assert "line 3, column 5" in msg
    assert "invalid mantissa" in msg

    with pytest.raises(tomllib.TOMLDecodeError) as exc:
        tomllib.loads("\n\n.")
    msg = str(exc.value)
    assert "line 3, column" in msg
    assert "missing value" in msg

    with pytest.raises(tomllib.TOMLDecodeError) as exc:
        tomllib.loads("val = 0b_1")
    msg = str(exc.value)
    assert "line 1, column 9" in msg
    assert "`_` may only go between digits, expected nothing" in msg


def test_missing_value() -> None:
    with pytest.raises(tomllib.TOMLDecodeError) as exc:
        tomllib.loads("\n\nfwfw=")
    msg = str(exc.value)
    assert "line 3, column 6" in msg
    assert "string values must be quoted" in msg


def test_invalid_char_quotes() -> None:
    with pytest.raises(tomllib.TOMLDecodeError) as exc:
        tomllib.loads("v = '\n'")
    assert "key with no value, expected `=`" in str(exc.value)


def test_type_error() -> None:
    with pytest.raises(TypeError) as exc:
        tomllib.loads(b"v = 1")  # ty: ignore[invalid-argument-type]]
    assert str(exc.value) in (
        "Expected str object, not 'bytes'", "str object expected; got bytes",
    )

    with pytest.raises(TypeError) as exc:
        tomllib.loads(False)  # ty: ignore[invalid-argument-type]  # noqa: FBT003
    assert str(exc.value) in (
        "Expected str object, not 'bool'", "str object expected; got bool",
    )


def test_invalid_parse_float() -> None:
    def dict_returner(s: str) -> dict[Any, Any]:
        return {}

    def list_returner(s: str) -> list[Any]:
        return []

    err_msg = "parse_float must not return dicts or lists"

    for invalid_parse_float in (dict_returner, list_returner):
        with pytest.raises(ValueError, match=err_msg) as exc:
            tomllib.loads("f=0.1", parse_float=invalid_parse_float)
        assert str(exc.value) == err_msg


def test_tomldecodeerror_attributes() -> None:
    data = _dedent("""
    title = "TOML Example"

    [owner]
    name = "Tom Preston-Werner"
    dob = 1979-05-27T07:32:00-08:00

    x =
    """)

    with pytest.raises(tomllib.TOMLDecodeError) as exc_info:
        tomllib.loads(data)

    exc = exc_info.value

    assert exc.msg.startswith("TOML parse error")
    assert exc.doc == data
    assert exc.pos == 96
    assert exc.lineno == 7
    assert exc.colno == 4
    assert f"line {exc.lineno}, column {exc.colno}" in str(exc)


def test_unsupported_version() -> None:
    with pytest.raises(
            ValueError,
            match="Unsupported TOML version",
    ):
        tomllib.loads("x = 1", toml_version="2")  # ty: ignore[invalid-argument-type]


def test_incorrect_suffix(toml_version: toml_rs._lib.TomlVersion) -> None:
    t1 = "x = -_1"

    with pytest.raises(
            tomllib.TOMLDecodeError,
            match="`_` may only go between digits, expected nothing",
    ):
        tomllib.loads(t1, toml_version=toml_version)
