"""Focused tests for pure URL resolution behavior in the executable module."""

import importlib.machinery
import importlib.util
import os
import pathlib
import subprocess
import sys
import tempfile
import unittest
from unittest import mock


SCRIPT_PATH = pathlib.Path(__file__).parents[1] / "git-tui"
LOADER = importlib.machinery.SourceFileLoader("git_tui", str(SCRIPT_PATH))
SPEC = importlib.util.spec_from_loader(LOADER.name, LOADER)
assert SPEC is not None
git_tui = importlib.util.module_from_spec(SPEC)
sys.modules[LOADER.name] = git_tui
LOADER.exec_module(git_tui)


class GiteaRemoteUrlTests(unittest.TestCase):
    def setUp(self) -> None:
        self.app = git_tui.GitTuiApp.__new__(git_tui.GitTuiApp)

    def test_resolves_ssh_url_from_https_base_url(self) -> None:
        remote_url = self.app.resolve_gitea_remote_url(
            "owner/project", "https://git.example.com", transport="ssh"
        )

        self.assertEqual(remote_url, "git@git.example.com:owner/project.git")

    def test_resolves_custom_ssh_user_and_port(self) -> None:
        remote_url = self.app.resolve_gitea_remote_url(
            "owner/project",
            "https://git.example.com/gitea",
            transport="ssh",
            ssh_user="forge",
            ssh_port="2222",
        )

        self.assertEqual(
            remote_url,
            "ssh://forge@git.example.com:2222/owner/project.git",
        )

    def test_still_supports_https(self) -> None:
        remote_url = self.app.resolve_gitea_remote_url(
            "owner/project", "https://git.example.com", transport="https"
        )

        self.assertEqual(
            remote_url,
            "https://git.example.com/owner/project.git",
        )

    def test_rejects_invalid_ssh_port(self) -> None:
        remote_url = self.app.resolve_gitea_remote_url(
            "owner/project",
            "https://git.example.com",
            transport="ssh",
            ssh_port="not-a-port",
        )

        self.assertEqual(remote_url, "")


class RepositorySyncTests(unittest.TestCase):
    def setUp(self) -> None:
        self.app = git_tui.GitTuiApp.__new__(git_tui.GitTuiApp)
        self.app.status_message = ""
        self.app.show_output = lambda *_args: None
        self.app.show_command_result = lambda *_args: None
        self.original_cwd = pathlib.Path.cwd()
        self.temp_dir = tempfile.TemporaryDirectory()
        self.root = pathlib.Path(self.temp_dir.name)

    def tearDown(self) -> None:
        os.chdir(self.original_cwd)
        self.temp_dir.cleanup()

    def git(self, cwd: pathlib.Path, *args: str) -> str:
        completed = subprocess.run(
            ["git", *args],
            cwd=cwd,
            check=True,
            capture_output=True,
            text=True,
        )
        return completed.stdout.strip()

    def init_worktree(self, path: pathlib.Path) -> None:
        path.mkdir()
        self.git(path, "init", "-b", "main")
        self.git(path, "config", "user.name", "Test User")
        self.git(path, "config", "user.email", "test@example.com")
        self.git(path, "config", "commit.gpgsign", "false")

    def test_sync_pushes_local_commit_to_empty_remote(self) -> None:
        remote = self.root / "remote.git"
        local = self.root / "local"
        remote.mkdir()
        self.git(remote, "init", "--bare")
        self.init_worktree(local)
        (local / "local.txt").write_text("local\n", encoding="utf-8")
        self.git(local, "add", "local.txt")
        self.git(local, "commit", "-m", "local")
        self.git(local, "remote", "add", "origin", str(remote))

        os.chdir(local)
        self.assertTrue(self.app.sync_origin_repository())
        self.assertEqual(
            self.git(remote, "rev-parse", "refs/heads/main"),
            self.git(local, "rev-parse", "HEAD"),
        )

    def test_sync_populates_unborn_local_repository_from_remote(self) -> None:
        remote = self.root / "remote.git"
        seed = self.root / "seed"
        local = self.root / "local"
        remote.mkdir()
        self.git(remote, "init", "--bare")
        self.git(remote, "symbolic-ref", "HEAD", "refs/heads/main")
        self.init_worktree(seed)
        (seed / "remote.txt").write_text("remote\n", encoding="utf-8")
        self.git(seed, "add", "remote.txt")
        self.git(seed, "commit", "-m", "remote")
        self.git(seed, "remote", "add", "origin", str(remote))
        self.git(seed, "push", "origin", "main")
        self.init_worktree(local)
        self.git(local, "remote", "add", "origin", str(remote))

        os.chdir(local)
        self.assertTrue(self.app.sync_origin_repository())
        self.assertEqual((local / "remote.txt").read_text(), "remote\n")
        self.assertEqual(
            self.git(local, "rev-parse", "HEAD"),
            self.git(remote, "rev-parse", "refs/heads/main"),
        )

    def test_sync_merges_diverged_histories_and_pushes_result(self) -> None:
        remote = self.root / "remote.git"
        seed = self.root / "seed"
        local = self.root / "local"
        remote.mkdir()
        self.git(remote, "init", "--bare")
        self.git(remote, "symbolic-ref", "HEAD", "refs/heads/main")
        self.init_worktree(seed)
        (seed / "remote.txt").write_text("remote\n", encoding="utf-8")
        self.git(seed, "add", "remote.txt")
        self.git(seed, "commit", "-m", "remote")
        self.git(seed, "remote", "add", "origin", str(remote))
        self.git(seed, "push", "origin", "main")
        self.init_worktree(local)
        (local / "local.txt").write_text("local\n", encoding="utf-8")
        self.git(local, "add", "local.txt")
        self.git(local, "commit", "-m", "local")
        self.git(local, "remote", "add", "origin", str(remote))
        self.app.select_option_menu = lambda *_args: "merge"

        os.chdir(local)
        self.assertTrue(self.app.sync_origin_repository())
        self.assertTrue((local / "local.txt").exists())
        self.assertTrue((local / "remote.txt").exists())
        self.assertEqual(
            self.git(local, "rev-parse", "HEAD"),
            self.git(remote, "rev-parse", "refs/heads/main"),
        )


class GiteaPublishTests(unittest.TestCase):
    def test_created_repository_can_keep_ssh_origin(self) -> None:
        app = git_tui.GitTuiApp.__new__(git_tui.GitTuiApp)
        app.status_message = ""
        prompt_values = iter(["owner/project", "https://git.example.com"])
        app.prompt_input = lambda *_args: next(prompt_values)
        app.select_gitea_transport = lambda: "ssh"
        app.select_repository_visibility = lambda: False
        app.create_gitea_repository = lambda **_kwargs: (
            True,
            {
                "clone_url": "https://git.example.com/owner/project.git",
                "ssh_url": "git@git.example.com:owner/project.git",
                "full_name": "owner/project",
            },
            "",
        )
        configured_urls = []
        app.configure_origin_remote = lambda url: configured_urls.append(url) or True
        app.push_head_to_remote_branch = lambda *_args, **_kwargs: True
        app.current_local_branch = lambda: "main"
        app.show_output = lambda *_args: None

        with mock.patch.dict(os.environ, {"GITEA_TOKEN": "token"}, clear=False):
            self.assertTrue(app.publish_gitea_repository())

        self.assertEqual(
            configured_urls,
            ["git@git.example.com:owner/project.git"],
        )


class MainMenuTests(unittest.TestCase):
    def test_remote_operations_and_disconnect_require_a_remote(self) -> None:
        app = git_tui.GitTuiApp.__new__(git_tui.GitTuiApp)
        app.has_any_remote = lambda: False

        action_names = [action for _, action in app.main_menu_items()]

        self.assertNotIn("push", action_names)
        self.assertNotIn("pull", action_names)
        self.assertNotIn("fetch", action_names)
        self.assertNotIn("disconnect_remote", action_names)

    def test_disconnect_is_available_when_a_remote_exists(self) -> None:
        app = git_tui.GitTuiApp.__new__(git_tui.GitTuiApp)
        app.has_any_remote = lambda: True

        action_names = [action for _, action in app.main_menu_items()]

        self.assertIn("push", action_names)
        self.assertIn("pull", action_names)
        self.assertIn("fetch", action_names)
        self.assertIn("disconnect_remote", action_names)

if __name__ == "__main__":
    unittest.main()
