# #region Test.Git.Url.Edge [C:3] [TYPE Module] [SEMANTICS test, git, url, parse, coverage]
# @BRIEF Edge case tests for GitServiceUrlMixin — URL parsing, host extraction, alignment, identity parsing.
# @RELATION BINDS_TO -> [GitServiceUrlMixin]

import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent / "src"))

from unittest.mock import MagicMock, patch

import pytest

from src.services.git._url import GitServiceUrlMixin


@pytest.fixture
def mixin():
    return GitServiceUrlMixin()


class TestExtractHttpHost:
    def test_none_or_empty(self, mixin):
        assert mixin._extract_http_host(None) is None
        assert mixin._extract_http_host("") is None
        assert mixin._extract_http_host("  ") is None

    def test_non_http_scheme(self, mixin):
        assert mixin._extract_http_host("ssh://git@example.com") is None
        assert mixin._extract_http_host("git@example.com:repo.git") is None

    def test_http_with_port(self, mixin):
        result = mixin._extract_http_host("https://example.com:8443/repo")
        assert result == "example.com:8443"

    def test_http_without_port(self, mixin):
        result = mixin._extract_http_host("https://example.com/repo")
        assert result == "example.com"

    def test_parse_error(self, mixin):
        # urlparse doesn't typically raise, but test the except path
        result = mixin._extract_http_host(None)
        assert result is None

    def test_no_hostname(self, mixin):
        with patch("src.services.git._url.urlparse") as mock_parse:
            parsed = MagicMock()
            parsed.scheme = "http"
            parsed.hostname = None
            mock_parse.return_value = parsed
            result = mixin._extract_http_host("http:///path")
            assert result is None


class TestStripUrlCredentials:
    def test_empty(self, mixin):
        assert mixin._strip_url_credentials("") == ""

    def test_no_credentials(self, mixin):
        url = "https://example.com/repo.git"
        assert mixin._strip_url_credentials(url) == url

    def test_with_credentials(self, mixin):
        url = "https://user:pass@example.com/repo.git"
        result = mixin._strip_url_credentials(url)
        assert "user" not in result
        assert "pass" not in result
        assert "example.com/repo.git" in result

    def test_non_http_scheme(self, mixin):
        url = "ssh://git@example.com/repo.git"
        assert mixin._strip_url_credentials(url) == url

    def test_parse_error(self, mixin):
        with patch("src.services.git._url.urlparse", side_effect=Exception("parse error")):
            url = "https://example.com/repo"
            result = mixin._strip_url_credentials(url)
            assert result == url


class TestReplaceHostInUrl:
    def test_empty_source(self, mixin):
        assert mixin._replace_host_in_url("", "https://new.com") is None

    def test_empty_config(self, mixin):
        assert mixin._replace_host_in_url("https://old.com/repo", "") is None

    def test_non_http_scheme(self, mixin):
        assert mixin._replace_host_in_url("ssh://old.com", "https://new.com") is None

    def test_no_hostname(self, mixin):
        result = mixin._replace_host_in_url("http:///path", "https://new.com")
        assert result is None

    def test_successful_replacement(self, mixin):
        result = mixin._replace_host_in_url(
            "https://old.com:8080/org/repo.git",
            "https://new.com:8443"
        )
        assert result is not None
        assert "new.com:8443" in result
        assert "old.com" not in result

    def test_preserves_credentials(self, mixin):
        result = mixin._replace_host_in_url(
            "https://user:pass@old.com/org/repo.git",
            "https://new.com"
        )
        assert result is not None
        assert "user" in result
        assert "new.com" in result

    def test_parse_error(self, mixin):
        with patch("src.services.git._url.urlparse", side_effect=Exception("parse error")):
            result = mixin._replace_host_in_url("https://old.com", "https://new.com")
            assert result is None


class TestParseRemoteRepoIdentity:
    def test_empty_url(self, mixin):
        with pytest.raises(Exception, match="empty"):
            mixin._parse_remote_repo_identity("")

    def test_ssh_url(self, mixin):
        result = mixin._parse_remote_repo_identity("git@github.com:owner/repo.git")
        assert result["owner"] == "owner"
        assert result["repo"] == "repo"
        assert result["full_name"] == "owner/repo"

    def test_https_url(self, mixin):
        result = mixin._parse_remote_repo_identity("https://github.com/owner/repo.git")
        assert result["owner"] == "owner"
        assert result["repo"] == "repo"

    def test_nested_path(self, mixin):
        result = mixin._parse_remote_repo_identity("https://github.com/org/team/repo.git")
        assert result["owner"] == "org"
        assert result["repo"] == "repo"
        assert result["namespace"] == "org/team"

    def test_no_git_suffix(self, mixin):
        result = mixin._parse_remote_repo_identity("https://github.com/owner/repo")
        assert result["repo"] == "repo"
        assert result["full_name"] == "owner/repo"

    def test_too_few_parts(self, mixin):
        with pytest.raises(Exception, match="Cannot parse"):
            mixin._parse_remote_repo_identity("https://github.com/owner")

    def test_ssh_with_no_colon(self, mixin):
        """SSH URL with no colon after host."""
        result = mixin._parse_remote_repo_identity("git@github.com/owner/repo.git")
        # This goes through urlparse path
        assert result["owner"] == "owner"
        assert result["repo"] == "repo"


class TestDeriveServerUrlFromRemote:
    def test_empty(self, mixin):
        assert mixin._derive_server_url_from_remote("") is None

    def test_ssh_url_returns_none(self, mixin):
        assert mixin._derive_server_url_from_remote("git@github.com:org/repo.git") is None

    def test_non_http_scheme(self, mixin):
        assert mixin._derive_server_url_from_remote("ftp://example.com/repo") is None

    def test_no_hostname(self, mixin):
        assert mixin._derive_server_url_from_remote("http:///path") is None

    def test_success_https(self, mixin):
        result = mixin._derive_server_url_from_remote("https://github.com/org/repo.git")
        assert result == "https://github.com"

    def test_with_port(self, mixin):
        result = mixin._derive_server_url_from_remote("https://gitea.local:3000/org/repo.git")
        assert result == "https://gitea.local:3000"


class TestNormalizeGitServerUrl:
    def test_empty_raises(self, mixin):
        with pytest.raises(Exception, match="required"):
            mixin._normalize_git_server_url("")

    def test_strips_trailing_slash(self, mixin):
        result = mixin._normalize_git_server_url("https://github.com/")
        assert result == "https://github.com"

    def test_preserves_no_slash(self, mixin):
        result = mixin._normalize_git_server_url("https://github.com")
        assert result == "https://github.com"


class TestAlignOriginHostWithConfig:
    def test_no_config_host(self, mixin):
        result = mixin._align_origin_host_with_config(1, MagicMock(), None, None, None)
        assert result is None

    def test_no_origin_host(self, mixin):
        result = mixin._align_origin_host_with_config(
            1, MagicMock(), "https://new.com", "", None
        )
        assert result is None

    def test_hosts_match(self, mixin):
        result = mixin._align_origin_host_with_config(
            1, MagicMock(), "https://same.com", "https://same.com/repo", None
        )
        assert result is None

    def test_hosts_mismatch_sets_url(self, mixin):
        origin = MagicMock()
        result = mixin._align_origin_host_with_config(
            1, origin, "https://new.com", "https://old.com/repo", None
        )
        assert result is not None
        assert "new.com" in result
        origin.set_url.assert_called_once()

    def test_set_url_failure(self, mixin):
        origin = MagicMock()
        origin.set_url.side_effect = Exception("set_url failed")
        result = mixin._align_origin_host_with_config(
            1, origin, "https://new.com", "https://old.com/repo", None
        )
        assert result is None

    def test_sets_aligned_url(self, mixin):
        origin = MagicMock()
        with patch("src.services.git._url.SessionLocal") as mock_sl:
            mock_db = MagicMock()
            mock_sl.return_value = mock_db
            mock_db_repo = MagicMock()
            mock_db.query.return_value.filter.return_value.first.return_value = mock_db_repo
            result = mixin._align_origin_host_with_config(
                1, origin, "https://new.com", "https://old.com/repo", None
            )
            assert result is not None
            mock_db_repo.remote_url = mixin._strip_url_credentials(result)
            assert mock_db_repo.commit.called

    def test_db_update_failure(self, mixin):
        origin = MagicMock()
        with patch("src.services.git._url.SessionLocal", side_effect=Exception("DB error")):
            result = mixin._align_origin_host_with_config(
                1, origin, "https://new.com", "https://old.com/repo", None
            )
            assert result is not None  # still returns aligned URL

    def test_uses_binding_remote_url_when_current_empty(self, mixin):
        origin = MagicMock()
        result = mixin._align_origin_host_with_config(
            1, origin, "https://new.com", "", "https://old.com/repo"
        )
        assert result is not None
        assert "old.com" in result
# #endregion Test.Git.Url.Edge
