#!/usr/bin/env python3
"""Account seeding preserves settings and shares machine-local global instructions."""

import json
import os
from pathlib import Path
import subprocess
import tempfile
import unittest


REPO = Path(__file__).resolve().parents[1]
PROVIDERS = (("claude", "CLAUDE.md", "settings.json"), ("codex", "AGENTS.md", "config.toml"))


class AccountSeedingTests(unittest.TestCase):
    def setUp(self):
        self.work = tempfile.TemporaryDirectory()
        self.addCleanup(self.work.cleanup)
        self.home = Path(self.work.name)
        self.env = {**os.environ, "HOME": str(self.home), "CLAUDE_MULTIACC_PYTHON": "python3"}
        for provider, _, _ in PROVIDERS:
            (self.home / f".{provider}").mkdir()
            self.env[f"{provider.upper()}_ACCOUNTS_ROOT"] = str(self.home / f".{provider}-accounts")

    def seed(self, provider, directory=None):
        account = directory or self.home / f".{provider}-accounts" / "acct-01"
        subprocess.run(
            ["bash", "-c", '. "$1"; seed_account_dir "$2"', "seed", str(REPO / "lib/common.sh"), str(account)],
            env={**self.env, "MULTIACC_PROVIDER": provider}, check=True, capture_output=True, text=True,
        )
        return account

    def test_instructions_are_shared_and_updates_remain_visible(self):
        for provider, name, _ in PROVIDERS:
            with self.subTest(provider=provider):
                source = self.home / f".{provider}" / name
                source.write_text("Global preferences\n")
                account = self.seed(provider)
                self.assertTrue((account / name).is_symlink())
                self.assertEqual((account / name).resolve(), source.resolve())
                source.write_text("Updated preferences\n")
                self.seed(provider)
                self.assertEqual((account / name).read_text(), "Updated preferences\n")

    def test_existing_files_and_links_are_preserved(self):
        for provider, name, _ in PROVIDERS:
            with self.subTest(provider=provider):
                (self.home / f".{provider}" / name).write_text("Global preferences\n")
                account = self.seed(provider)
                target = account / name
                target.unlink()
                target.write_text("Account preferences\n")
                self.seed(provider)
                self.assertFalse(target.is_symlink())
                self.assertEqual(target.read_text(), "Account preferences\n")
                target.unlink()
                local = self.home / f"{provider}-local.md"
                target.symlink_to(local)
                self.seed(provider)
                self.assertEqual(target.readlink(), local)  # Dangling custom link is also owned by the user.
                local.write_text("Custom preferences\n")
                self.seed(provider)
                self.assertEqual(target.read_text(), "Custom preferences\n")

    def test_missing_global_file_never_creates_a_dangling_or_self_link(self):
        for provider, name, _ in PROVIDERS:
            with self.subTest(provider=provider):
                account = self.seed(provider)
                self.assertFalse(os.path.lexists(account / name))
                base = self.home / f".{provider}"
                self.seed(provider, base)
                self.assertFalse(os.path.lexists(base / name))
                (base / name).write_text("Global preferences\n")
                self.seed(provider, base)
                self.assertFalse((base / name).is_symlink())

    def test_existing_settings_and_model_are_not_overwritten(self):
        for provider, _, name in PROVIDERS:
            with self.subTest(provider=provider):
                source = self.home / f".{provider}" / name
                source.write_text('{"model":"global"}\n' if provider == "claude" else 'model = "global"\n')
                account = self.seed(provider)
                self.assertEqual((account / name).read_bytes(), source.read_bytes())
                original = '{"model":"account"}\n' if provider == "claude" else 'model = "account"\n'
                (account / name).write_text(original)
                self.seed(provider)
                self.assertEqual((account / name).read_text(), original)

    def test_local_sync_repairs_only_manifest_accounts(self):
        for provider, name, _ in PROVIDERS:
            with self.subTest(provider=provider):
                (self.home / f".{provider}" / name).write_text("Global preferences\n")
                root = self.home / f".{provider}-accounts"
                (root / "acct-01").mkdir(parents=True)
                (root / "acct-99").mkdir()
                (root / "accounts.json").write_text(json.dumps({
                    "version": 1, "server": "none",
                    "accounts": [{"id": "acct-01", "email": "fixture@example.test", "home": "mac"}],
                }))
                for _ in range(2):
                    result = subprocess.run(
                        [str(REPO / "bin" / f"{provider}-accounts"), "sync", "--no-server"],
                        env=self.env, check=True, capture_output=True, text=True,
                    )
                    self.assertIn("nothing pushed", result.stdout)
                self.assertTrue((root / "acct-01" / name).is_symlink())
                self.assertFalse(os.path.lexists(root / "acct-99" / name))

    def test_seeding_applies_the_pool_mcp_registry(self):
        """The registry reaches a fresh account at seed time; an account's own servers survive."""
        registry = {"version": 1, "retired": [], "projects": {}, "mcpServers": {
            "seeded": {"type": "stdio", "command": "echo", "args": ["hi"], "env": {"K": "v"}}}}
        for provider, name in (("claude", ".claude.json"), ("codex", "config.toml")):
            with self.subTest(provider=provider):
                root = self.home / f".{provider}-accounts"
                root.mkdir(exist_ok=True)
                (root / "accounts.json").write_text(json.dumps({
                    "version": 1, "server": "none",
                    "accounts": [{"id": "acct-01", "email": "fixture@example.test", "home": "mac"}]}))
                (root / "mcp-servers.json").write_text(json.dumps(registry))
                account = root / "acct-01"
                account.mkdir()
                if provider == "claude":
                    (account / name).write_text(json.dumps(
                        {"mcpServers": {"own": {"type": "stdio", "command": "true", "args": [], "env": {}}}}))
                else:
                    (account / name).write_text('model = "gpt-5"\n\n[mcp_servers.own]\ncommand = "true"\nargs = []\n')
                for _ in range(2):
                    self.seed(provider, account)
                    text = (account / name).read_text()
                    if provider == "claude":
                        servers = json.loads(text)["mcpServers"]
                        self.assertEqual(servers["seeded"]["command"], "echo")
                        self.assertIn("own", servers)
                    else:
                        self.assertIn("[mcp_servers.seeded]", text)
                        self.assertIn('command = "echo"', text)
                        self.assertIn("[mcp_servers.own]", text)
                        self.assertIn('model = "gpt-5"', text)
                self.assertTrue((account / ".mcp-applied").exists())

    def test_seeding_without_a_registry_touches_no_config(self):
        for provider, _, name in PROVIDERS:
            with self.subTest(provider=provider):
                account = self.seed(provider)
                self.assertFalse((account / ".mcp-applied").exists())
                self.assertFalse((self.home / f".{provider}-accounts" / "mcp-servers.json").exists())


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