# #region Test.Git.Url [C:3] [TYPE Module] [SEMANTICS test,git,url,parse,remote]
# @BRIEF Tests for GitServiceUrlMixin — host extraction, credential stripping, host replacement, origin alignment, remote identity parsing, server URL derivation, URL normalization.
# @RELATION BINDS_TO -> [GitServiceUrlMixin]
# @TEST_EDGE: empty_url -> returns None or empty
# @TEST_EDGE: non_http_scheme -> returns None (ssh/git protocol)
# @TEST_EDGE: url_with_port -> host:port preserved
# @TEST_EDGE: url_with_credentials -> credentials stripped/replaced correctly
# @TEST_EDGE: ssh_url -> parsed via git@ split
# @TEST_EDGE: missing_owner_repo -> raises HTTPException 400
# @TEST_EDGE: empty_server_url -> raises HTTPException 400

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

import pytest
from unittest.mock import MagicMock, patch

from fastapi import HTTPException

from src.services.git._url import GitServiceUrlMixin


class TestableGitUrl(GitServiceUrlMixin):
    """Minimal concrete subclass for testing URL mixin methods."""
    pass


# ── _extract_http_host ──

class TestExtractHttpHost:
    """_extract_http_host — host[:port] extraction from HTTP(S) URLs."""

    # #region test_extract_host_https [C:2] [TYPE Function]
    def test_extract_host_https(self):
        """Standard HTTPS URL returns lowercase host."""
        svc = TestableGitUrl()
        result = svc._extract_http_host("https://Git.Example.COM/org/repo.git")
        assert result == "git.example.com"
    # #endregion test_extract_host_https

    # #region test_extract_host_with_port [C:2] [TYPE Function]
    def test_extract_host_with_port(self):
        """URL with port returns host:port."""
        svc = TestableGitUrl()
        result = svc._extract_http_host("http://gitea.local:3000/repo")
        assert result == "gitea.local:3000"
    # #endregion test_extract_host_with_port

    # #region test_extract_host_empty [C:2] [TYPE Function]
    def test_extract_host_empty(self):
        """Empty/None input returns None."""
        svc = TestableGitUrl()
        assert svc._extract_http_host(None) is None
        assert svc._extract_http_host("") is None
        assert svc._extract_http_host("   ") is None
    # #endregion test_extract_host_empty

    # #region test_extract_host_non_http [C:2] [TYPE Function]
    def test_extract_host_non_http(self):
        """SSH or FTP scheme returns None."""
        svc = TestableGitUrl()
        assert svc._extract_http_host("git@github.com:org/repo.git") is None
        assert svc._extract_http_host("ftp://files.example.com/repo") is None
    # #endregion test_extract_host_non_http

    # #region test_extract_host_no_hostname [C:2] [TYPE Function]
    def test_extract_host_no_hostname(self):
        """Malformed URL with no hostname returns None."""
        svc = TestableGitUrl()
        assert svc._extract_http_host("http://") is None
    # #endregion test_extract_host_no_hostname


# ── _strip_url_credentials ──

class TestStripUrlCredentials:
    """_strip_url_credentials — remove user:pass from URL."""

    # #region test_strip_credentials [C:2] [TYPE Function]
    def test_strip_credentials(self):
        """Credentials removed, scheme/host/path preserved."""
        svc = TestableGitUrl()
        result = svc._strip_url_credentials("https://oauth2:token123@git.example.com/org/repo.git")
        assert result == "https://git.example.com/org/repo.git"
    # #endregion test_strip_credentials

    # #region test_strip_no_credentials [C:2] [TYPE Function]
    def test_strip_no_credentials(self):
        """URL without credentials returned unchanged."""
        svc = TestableGitUrl()
        result = svc._strip_url_credentials("https://git.example.com/org/repo.git")
        assert result == "https://git.example.com/org/repo.git"
    # #endregion test_strip_no_credentials

    # #region test_strip_empty [C:2] [TYPE Function]
    def test_strip_empty(self):
        """Empty string returned as-is."""
        svc = TestableGitUrl()
        assert svc._strip_url_credentials("") == ""
    # #endregion test_strip_empty

    # #region test_strip_non_http_passthrough [C:2] [TYPE Function]
    def test_strip_non_http_passthrough(self):
        """Non-HTTP URL returned unchanged."""
        svc = TestableGitUrl()
        url = "git@github.com:org/repo.git"
        assert svc._strip_url_credentials(url) == url
    # #endregion test_strip_non_http_passthrough

    # #region test_strip_with_port [C:2] [TYPE Function]
    def test_strip_with_port(self):
        """Port preserved after credential stripping."""
        svc = TestableGitUrl()
        result = svc._strip_url_credentials("https://user:pass@gitea.local:3000/repo")
        assert result == "https://gitea.local:3000/repo"
    # #endregion test_strip_with_port


# ── _replace_host_in_url ──

class TestReplaceHostInUrl:
    """_replace_host_in_url — swap origin host with config host."""

    # #region test_replace_host_basic [C:2] [TYPE Function]
    def test_replace_host_basic(self):
        """Source host replaced with config host, credentials preserved."""
        svc = TestableGitUrl()
        result = svc._replace_host_in_url(
            "https://old-host.com/org/repo.git",
            "https://new-host.com",
        )
        assert result == "https://new-host.com/org/repo.git"
    # #endregion test_replace_host_basic

    # #region test_replace_host_preserves_auth [C:2] [TYPE Function]
    def test_replace_host_preserves_auth(self):
        """Credentials from source URL preserved on new host."""
        svc = TestableGitUrl()
        result = svc._replace_host_in_url(
            "https://oauth2:tok123@old-host.com/org/repo.git",
            "https://new-host.com:3000",
        )
        assert "oauth2:tok123@new-host.com:3000" in result
        assert "/org/repo.git" in result
    # #endregion test_replace_host_preserves_auth

    # #region test_replace_host_empty_inputs [C:2] [TYPE Function]
    def test_replace_host_empty_inputs(self):
        """Empty source or config returns None."""
        svc = TestableGitUrl()
        assert svc._replace_host_in_url(None, "https://x.com") is None
        assert svc._replace_host_in_url("https://x.com", None) is None
        assert svc._replace_host_in_url("", "") is None
    # #endregion test_replace_host_empty_inputs

    # #region test_replace_host_non_http [C:2] [TYPE Function]
    def test_replace_host_non_http(self):
        """Non-HTTP source or config returns None."""
        svc = TestableGitUrl()
        assert svc._replace_host_in_url("git@old:x.git", "https://new.com") is None
        assert svc._replace_host_in_url("https://old.com/x", "ftp://new.com") is None
    # #endregion test_replace_host_non_http


# ── _parse_remote_repo_identity ──

class TestParseRemoteRepoIdentity:
    """_parse_remote_repo_identity — extract owner/repo from remote URL."""

    # #region test_parse_https_url [C:2] [TYPE Function]
    def test_parse_https_url(self):
        """HTTPS URL parsed into owner, repo, namespace."""
        svc = TestableGitUrl()
        result = svc._parse_remote_repo_identity("https://gitea.example.com/org/repo.git")
        assert result["owner"] == "org"
        assert result["repo"] == "repo"
        assert result["namespace"] == "org"
        assert result["full_name"] == "org/repo"
    # #endregion test_parse_https_url

    # #region test_parse_ssh_url [C:2] [TYPE Function]
    def test_parse_ssh_url(self):
        """SSH git@ URL parsed via colon split."""
        svc = TestableGitUrl()
        result = svc._parse_remote_repo_identity("git@github.com:myorg/myrepo.git")
        assert result["owner"] == "myorg"
        assert result["repo"] == "myrepo"
        assert result["namespace"] == "myorg"
    # #endregion test_parse_ssh_url

    # #region test_parse_nested_namespace [C:2] [TYPE Function]
    def test_parse_nested_namespace(self):
        """Nested namespace (subgroups) preserved."""
        svc = TestableGitUrl()
        result = svc._parse_remote_repo_identity("https://gitlab.com/group/subgroup/project.git")
        assert result["owner"] == "group"
        assert result["repo"] == "project"
        assert result["namespace"] == "group/subgroup"
        assert result["full_name"] == "group/subgroup/project"
    # #endregion test_parse_nested_namespace

    # #region test_parse_empty_url_raises [C:2] [TYPE Function]
    def test_parse_empty_url_raises(self):
        """Empty URL raises HTTPException 400."""
        svc = TestableGitUrl()
        with pytest.raises(HTTPException, match="empty"):
            svc._parse_remote_repo_identity("")
    # #endregion test_parse_empty_url_raises

    # #region test_parse_single_segment_raises [C:2] [TYPE Function]
    def test_parse_single_segment_raises(self):
        """URL with <2 path segments raises HTTPException 400."""
        svc = TestableGitUrl()
        with pytest.raises(HTTPException, match="Cannot parse"):
            svc._parse_remote_repo_identity("https://host.com/onlyone")
    # #endregion test_parse_single_segment_raises

    # #region test_parse_no_git_suffix [C:2] [TYPE Function]
    def test_parse_no_git_suffix(self):
        """URL without .git suffix parsed correctly."""
        svc = TestableGitUrl()
        result = svc._parse_remote_repo_identity("https://host.com/owner/repo")
        assert result["repo"] == "repo"
    # #endregion test_parse_no_git_suffix


# ── _derive_server_url_from_remote ──

class TestDeriveServerUrlFromRemote:
    """_derive_server_url_from_remote — build API base URL."""

    # #region test_derive_https [C:2] [TYPE Function]
    def test_derive_https(self):
        """HTTPS URL → scheme://host base."""
        svc = TestableGitUrl()
        result = svc._derive_server_url_from_remote("https://gitea.example.com/org/repo.git")
        assert result == "https://gitea.example.com"
    # #endregion test_derive_https

    # #region test_derive_with_port [C:2] [TYPE Function]
    def test_derive_with_port(self):
        """Port preserved in derived URL."""
        svc = TestableGitUrl()
        result = svc._derive_server_url_from_remote("http://gitea.local:3000/repo")
        assert result == "http://gitea.local:3000"
    # #endregion test_derive_with_port

    # #region test_derive_ssh_returns_none [C:2] [TYPE Function]
    def test_derive_ssh_returns_none(self):
        """SSH URL returns None."""
        svc = TestableGitUrl()
        assert svc._derive_server_url_from_remote("git@host:org/repo.git") is None
    # #endregion test_derive_ssh_returns_none

    # #region test_derive_empty_returns_none [C:2] [TYPE Function]
    def test_derive_empty_returns_none(self):
        """Empty input returns None."""
        svc = TestableGitUrl()
        assert svc._derive_server_url_from_remote("") is None
        assert svc._derive_server_url_from_remote(None) is None
    # #endregion test_derive_empty_returns_none


# ── _normalize_git_server_url ──

class TestNormalizeGitServerUrl:
    """_normalize_git_server_url — strip trailing slash."""

    # #region test_normalize_strips_slash [C:2] [TYPE Function]
    def test_normalize_strips_slash(self):
        """Trailing slash removed."""
        svc = TestableGitUrl()
        assert svc._normalize_git_server_url("https://gitea.com/") == "https://gitea.com"
    # #endregion test_normalize_strips_slash

    # #region test_normalize_no_change [C:2] [TYPE Function]
    def test_normalize_no_change(self):
        """URL without trailing slash unchanged."""
        svc = TestableGitUrl()
        assert svc._normalize_git_server_url("https://gitea.com") == "https://gitea.com"
    # #endregion test_normalize_no_change

    # #region test_normalize_empty_raises [C:2] [TYPE Function]
    def test_normalize_empty_raises(self):
        """Empty URL raises HTTPException 400."""
        svc = TestableGitUrl()
        with pytest.raises(HTTPException, match="required"):
            svc._normalize_git_server_url("")
    # #endregion test_normalize_empty_raises


# ── _align_origin_host_with_config ──

class TestAlignOriginHostWithConfig:
    """_align_origin_host_with_config — auto-align origin host drift."""

    # #region test_align_no_drift [C:2] [TYPE Function]
    def test_align_no_drift(self):
        """Matching hosts → no action, returns None."""
        svc = TestableGitUrl()
        origin = MagicMock()
        result = svc._align_origin_host_with_config(
            dashboard_id=1,
            origin=origin,
            config_url="https://gitea.com",
            current_origin_url="https://gitea.com/org/repo.git",
            binding_remote_url=None,
        )
        assert result is None
        origin.set_url.assert_not_called()
    # #endregion test_align_no_drift

    # #region test_align_host_mismatch [C:2] [TYPE Function]
    @patch("src.services.git._url.SessionLocal")
    def test_align_host_mismatch(self, mock_session_cls):
        """Mismatched hosts → origin.set_url called, DB updated."""
        mock_session = MagicMock()
        mock_session_cls.return_value = mock_session
        mock_session.query.return_value.filter.return_value.first.return_value = None

        svc = TestableGitUrl()
        origin = MagicMock()
        result = svc._align_origin_host_with_config(
            dashboard_id=1,
            origin=origin,
            config_url="https://new-host.com",
            current_origin_url="https://old-host.com/org/repo.git",
            binding_remote_url=None,
        )
        assert result is not None
        assert "new-host.com" in result
        origin.set_url.assert_called_once()
    # #endregion test_align_host_mismatch

    # #region test_align_missing_config [C:2] [TYPE Function]
    def test_align_missing_config(self):
        """No config URL → returns None, no action."""
        svc = TestableGitUrl()
        origin = MagicMock()
        result = svc._align_origin_host_with_config(
            dashboard_id=1, origin=origin, config_url=None,
            current_origin_url="https://host.com/repo", binding_remote_url=None,
        )
        assert result is None
    # #endregion test_align_missing_config
# #endregion Test.Git.Url
