"""Tests for prodigy_teams.util — URL parsing, path helpers, and regex patterns."""

from __future__ import annotations

import pytest

from prodigy_teams.util import (
    BUCKET_PATTERN,
    URL,
    _is_root_path,
    collapse_path_aliases,
    get_app_dir,
    is_local,
    parse_remote_path,
)

# ---------------------------------------------------------------------------
# URL.parse
# ---------------------------------------------------------------------------


class TestURLParse:
    def test_plain_host(self):
        u = URL.parse("example.com")
        assert u.scheme == "https"
        assert u.netloc == "example.com"
        assert u.port == 443
        assert u.url == "https://example.com"

    def test_host_with_port(self):
        u = URL.parse("example.com:8080")
        assert u.port == 8080
        assert u.netloc == "example.com:8080"

    def test_http_scheme(self):
        u = URL.parse("http://example.com")
        assert u.scheme == "http"
        assert u.port == 80

    def test_https_explicit(self):
        u = URL.parse("https://example.com")
        assert u.scheme == "https"
        assert u.port == 443

    def test_strips_whitespace_and_trailing_slash(self):
        u = URL.parse("  https://example.com/  ")
        assert u.url == "https://example.com"

    def test_double_https_protocol(self):
        u = URL.parse("https://https://example.com")
        assert u.url == "https://example.com"
        assert u.scheme == "https"

    def test_double_http_protocol(self):
        u = URL.parse("http://http://example.com")
        assert u.url == "http://example.com"
        assert u.scheme == "http"

    def test_url_with_path(self):
        u = URL.parse("https://app.example.com:8080/path/to/resource")
        assert u.url == "https://app.example.com:8080/path/to/resource"
        assert u.netloc == "app.example.com:8080"
        assert u.port == 8080

    def test_default_scheme_override(self):
        u = URL.parse("example.com", scheme="http")
        assert u.scheme == "http"
        assert u.port == 80

    def test_unsupported_scheme_raises(self):
        with pytest.raises(ValueError, match="unsupported url scheme"):
            URL._default_port_from_scheme("ftp")


class TestURLOperators:
    def test_format(self):
        u = URL.parse("https://example.com")
        assert f"{u}" == "https://example.com"

    def test_str(self):
        u = URL.parse("https://example.com")
        assert str(u) == "https://example.com"

    def test_truediv(self):
        u = URL.parse("https://example.com")
        sub = u / "api/v1"
        assert sub.url == "https://example.com/api/v1"
        assert sub.netloc == "example.com"
        assert sub.port == 443

    def test_truediv_strips_slashes(self):
        u = URL.parse("https://example.com")
        sub = u / "/api/v1/"
        assert sub.url == "https://example.com/api/v1"


# ---------------------------------------------------------------------------
# is_local
# ---------------------------------------------------------------------------


class TestIsLocal:
    def test_curly_brace_is_remote(self):
        assert is_local("{bucket}/path") is False

    def test_s3_is_remote(self):
        assert is_local("s3://my-bucket/path") is False

    def test_gs_is_remote(self):
        assert is_local("gs://my-bucket/path") is False

    def test_relative_path_is_local(self):
        assert is_local("some/path") is True

    def test_absolute_path_is_local(self):
        assert is_local("/tmp/data") is True

    def test_dot_path_is_local(self):
        assert is_local("./data") is True


# ---------------------------------------------------------------------------
# collapse_path_aliases
# ---------------------------------------------------------------------------


class TestCollapsePathAliases:
    def test_basic_alias(self):
        aliases = {"data": "/mnt/nfs/data"}
        assert (
            collapse_path_aliases("/mnt/nfs/data/file.txt", aliases)
            == "{data}/file.txt"
        )

    def test_no_match(self):
        aliases = {"data": "/mnt/nfs/data"}
        assert (
            collapse_path_aliases("/other/path/file.txt", aliases)
            == "/other/path/file.txt"
        )

    def test_longest_match_wins(self):
        aliases = {
            "root": "/mnt",
            "data": "/mnt/nfs/data",
        }
        assert (
            collapse_path_aliases("/mnt/nfs/data/file.txt", aliases)
            == "{data}/file.txt"
        )

    def test_trailing_slash_stripped(self):
        aliases = {"data": "/mnt/nfs/data/"}
        assert (
            collapse_path_aliases("/mnt/nfs/data/file.txt", aliases)
            == "{data}/file.txt"
        )


# ---------------------------------------------------------------------------
# parse_remote_path
# ---------------------------------------------------------------------------


class TestParseRemotePath:
    def test_plain_path(self):
        broker, name, subpath = parse_remote_path("some/path")
        assert broker is None
        assert name is None
        assert subpath == "some/path"

    def test_with_name(self):
        broker, name, subpath = parse_remote_path("{my_path}/sub/dir")
        assert broker is None
        assert name == "my_path"
        assert subpath == "sub/dir"

    def test_with_broker_and_name(self):
        broker, name, subpath = parse_remote_path("{my_broker}:{my_path}/sub/dir")
        assert broker == "my_broker"
        assert name == "my_path"
        assert subpath == "sub/dir"

    def test_builtin_path(self):
        broker, name, subpath = parse_remote_path("{__data__}/sub/dir")
        assert broker is None
        assert name == "__data__"
        assert subpath == "sub/dir"

    def test_invalid_path_raises(self):
        with pytest.raises(ValueError):
            parse_remote_path("{bad{nested}")

    def test_name_only_no_subpath(self):
        broker, name, subpath = parse_remote_path("{my_path}/")
        assert broker is None
        assert name == "my_path"
        assert subpath == ""

    def test_empty_subpath(self):
        broker, name, subpath = parse_remote_path("{my_path}")
        assert broker is None
        assert name == "my_path"
        assert subpath == ""


# ---------------------------------------------------------------------------
# BUCKET_PATTERN
# ---------------------------------------------------------------------------


class TestBucketPattern:
    def test_s3_bucket(self):
        m = BUCKET_PATTERN.match("s3://my-bucket/some/path")
        assert m is not None
        assert m.group("cloud_proto") == "s3"
        assert m.group("cloud_bucket") == "my-bucket"
        assert m.group("path") == "some/path"

    def test_gs_bucket(self):
        m = BUCKET_PATTERN.match("gs://my-bucket/data")
        assert m is not None
        assert m.group("cloud_proto") == "gs"
        assert m.group("path") == "data"

    def test_posix_root(self):
        m = BUCKET_PATTERN.match("/absolute/path")
        assert m is not None
        assert m.group("posix_root") == "/"
        assert m.group("path") == "absolute/path"

    def test_alias_root(self):
        m = BUCKET_PATTERN.match("{my_alias}/data/file")
        assert m is not None
        assert m.group("alias_root") == "{my_alias}"
        assert m.group("path") == "data/file"

    def test_no_match(self):
        assert BUCKET_PATTERN.match("relative/path") is None


# ---------------------------------------------------------------------------
# _is_root_path
# ---------------------------------------------------------------------------


class TestIsRootPath:
    def test_s3_root(self):
        assert _is_root_path("s3://my-bucket") is True

    def test_s3_with_path(self):
        assert _is_root_path("s3://my-bucket/data") is False

    def test_posix_root(self):
        assert _is_root_path("/") is True

    def test_posix_with_path(self):
        assert _is_root_path("/data") is False

    def test_alias_root(self):
        assert _is_root_path("{my_alias}") is True

    def test_alias_with_path(self):
        assert _is_root_path("{my_alias}/data") is False

    def test_invalid_path_raises(self):
        with pytest.raises(ValueError, match="failed to parse"):
            _is_root_path("relative/path")


# ---------------------------------------------------------------------------
# get_app_dir
# ---------------------------------------------------------------------------


class TestGetAppDir:
    def test_returns_string(self):
        result = get_app_dir("test-app")
        assert isinstance(result, str)

    def test_force_posix(self):
        result = get_app_dir("Test App", force_posix=True)
        assert ".test-app" in result
