# Copyright © The Debusine Developers
# See the AUTHORS file at the top-level directory of this distribution
#
# This file is part of Debusine. It is subject to the license terms
# in the LICENSE file found in the top-level directory of this
# distribution. No part of Debusine, including this file, may be copied,
# modified, propagated, or distributed except according to the terms
# contained in the LICENSE file.

"""Tests for urls module."""

import re
from typing import Any

import pytest

from debusine.test import TestCase
from debusine.utils.urls import URLPath, parse_timeout


class URLPathTests(TestCase):
    """Tests for the :py:class:`URLPath` class."""

    def test_init(self) -> None:
        for args, exp_value, exp_is_file, exp_str in (
            ([""], [], False, "/"),
            (["/"], [], False, "/"),
            (["//"], [], False, "/"),
            (["foo"], ["foo"], True, "foo"),
            (["foo/"], ["foo"], False, "foo/"),
            (["foo/bar.html"], ["foo", "bar.html"], True, "foo/bar.html"),
            (["foo/bar.html/"], ["foo", "bar.html"], False, "foo/bar.html/"),
            (["/foo//bar/"], ["foo", "bar"], False, "foo/bar/"),
            (["/foo//bar/", True], ["foo", "bar"], True, "foo/bar"),
            (["/foo//bar", False], ["foo", "bar"], False, "foo/bar/"),
        ):
            with self.subTest(args=args):
                upath = URLPath(*args)
                self.assertEqual(upath.path, exp_value)
                self.assertEqual(upath.is_file, exp_is_file)
                self.assertEqual(str(upath), exp_str)

    def test_sequence(self) -> None:
        upath = URLPath("/foo/bar.html")

        # Container
        self.assertIn("foo", upath)
        self.assertNotIn("bar", upath)

        # Sized
        self.assertEqual(len(upath), 2)

        # Iterable
        self.assertEqual(list(upath), ["foo", "bar.html"])

        # Sequence
        self.assertEqual(upath[0], "foo")
        self.assertEqual(upath[1], "bar.html")
        self.assertEqual(upath[0:2], ["foo", "bar.html"])

    def test_add(self) -> None:
        for first, second, expected in (
            ("", "", "/"),
            ("/foo.html", "bar.html", "bar.html"),
            ("/foo.html", "api/", "api/"),
            ("/api/", "/send", "api/send"),
            ("/api/", "/send/", "api/send/"),
        ):
            with self.subTest(first=str(first), second=str(second)):
                # Add URLPath
                self.assertEqual(
                    str(URLPath(first) + URLPath(second)), expected
                )

                # In-place add URLPath
                p = URLPath(first)
                p += URLPath(second)
                self.assertEqual(str(p), expected)

                # Add str
                self.assertEqual(str(URLPath(first) + second), expected)

                # In-place add str
                p = URLPath(first)
                p += second
                self.assertEqual(str(p), expected)

    def test_add_invalid_type(self) -> None:
        upath = URLPath()
        value: Any
        for value in (None, 42, (), [], {}):
            with self.subTest(value=repr(value)):
                with self.assertRaisesRegex(
                    TypeError, re.escape("unsupported operand type(s) for +")
                ):
                    upath + value
                with self.assertRaisesRegex(
                    TypeError, re.escape("unsupported operand type(s) for +=")
                ):
                    upath += value

    def test_bool(self) -> None:
        for value, expected in (
            ("", False),
            ("/", False),
            ("/api", True),
            ("/api/", True),
        ):
            with self.subTest(value=value):
                self.assertEqual(bool(URLPath(value)), expected)

    def test_pop(self) -> None:
        for value, expected in (
            ("", "/"),
            ("/", "/"),
            ("/api", "/"),
            ("/api/", "/"),
            ("/foo/bar.html", "foo/"),
            ("/foo/bar/", "foo/"),
        ):
            with self.subTest(value=value):
                upath = URLPath(value)
                upath.pop()
                self.assertEqual(str(upath), expected)


def test_parse_timeout() -> None:
    """Test parse_timeout utility."""
    assert parse_timeout("30") == 30
    assert parse_timeout("10, 20") == (10, 20)
    assert parse_timeout(" 5 , 15 ") == (5, 15)

    with pytest.raises(ValueError, match="Invalid timeout value"):
        parse_timeout("invalid")
    with pytest.raises(ValueError, match="Invalid timeout value"):
        parse_timeout("10, invalid")
