"""Tests for account manager — CRUD, usage, budget, serialization."""

import json
import pytest

from proxym.accounts import (
    Account,
    AccountCredentials,
    AccountLimits,
    AccountManager,
    ProviderType,
    ToolBinding,
    ToolType,
    UsageMetrics,
)


@pytest.fixture
def acct_mgr(tmp_path):
    return AccountManager(config_path=str(tmp_path / "accounts.json"))


@pytest.fixture
def sample_account():
    return Account(
        name="Anthropic Work",
        provider=ProviderType.ANTHROPIC,
        email="dev@example.com",
        plan_tier="pro",
        credentials=AccountCredentials(api_key="sk-ant-test1234567890abcdef"),
        limits=AccountLimits(
            user_daily_budget_usd=5.0,
            user_monthly_budget_usd=60.0,
        ),
        tags=["work"],
    )


class TestAccountCRUD:
    def test_add_and_get(self, acct_mgr, sample_account):
        added = acct_mgr.add_account(sample_account)
        assert added.id == sample_account.id
        fetched = acct_mgr.get_account(added.id)
        assert fetched is not None
        assert fetched.name == "Anthropic Work"

    def test_list_accounts(self, acct_mgr, sample_account):
        acct_mgr.add_account(sample_account)
        acct_mgr.add_account(Account(
            name="OpenAI Personal",
            provider=ProviderType.OPENAI,
            credentials=AccountCredentials(api_key="sk-openai-test"),
            tags=["personal"],
        ))
        assert len(acct_mgr.list_accounts()) == 2

    def test_filter_by_provider(self, acct_mgr, sample_account):
        acct_mgr.add_account(sample_account)
        acct_mgr.add_account(Account(
            name="OpenAI",
            provider=ProviderType.OPENAI,
            credentials=AccountCredentials(api_key="sk-test"),
        ))
        anthro = acct_mgr.list_accounts(provider=ProviderType.ANTHROPIC)
        assert len(anthro) == 1
        assert anthro[0].provider == ProviderType.ANTHROPIC

    def test_filter_by_tag(self, acct_mgr, sample_account):
        acct_mgr.add_account(sample_account)
        acct_mgr.add_account(Account(
            name="Personal",
            provider=ProviderType.OPENAI,
            credentials=AccountCredentials(api_key="sk-test"),
            tags=["personal"],
        ))
        work = acct_mgr.list_accounts(tag="work")
        assert len(work) == 1
        assert work[0].name == "Anthropic Work"

    def test_remove_account(self, acct_mgr, sample_account):
        acct_mgr.add_account(sample_account)
        assert acct_mgr.remove_account(sample_account.id) is True
        assert acct_mgr.get_account(sample_account.id) is None
        assert acct_mgr.remove_account("nonexistent") is False

    def test_bind_tool(self, acct_mgr, sample_account):
        acct_mgr.add_account(sample_account)
        binding = ToolBinding(
            tool_type=ToolType.ROO_CODE,
            tool_name="Roo Code Main",
            vm_id="vm-123",
            project_path="/home/user/myproject",
        )
        assert acct_mgr.bind_tool(sample_account.id, binding) is True
        acct = acct_mgr.get_account(sample_account.id)
        assert len(acct.tools) == 1
        assert acct.tools[0].tool_type == ToolType.ROO_CODE


class TestUsageTracking:
    def test_record_usage(self, acct_mgr, sample_account):
        acct_mgr.add_account(sample_account)
        acct_mgr.record_usage(sample_account.id, 1000, 500, 0.05)
        acct = acct_mgr.get_account(sample_account.id)
        assert acct.usage.total_tokens_in == 1000
        assert acct.usage.total_tokens_out == 500
        assert acct.usage.total_cost_usd == pytest.approx(0.05)
        assert acct.usage.total_requests == 1

    def test_daily_accumulation(self, acct_mgr, sample_account):
        acct_mgr.add_account(sample_account)
        acct_mgr.record_usage(sample_account.id, 1000, 500, 0.03)
        acct_mgr.record_usage(sample_account.id, 2000, 1000, 0.07)
        acct = acct_mgr.get_account(sample_account.id)
        assert acct.usage.daily_cost_usd == pytest.approx(0.10)
        assert acct.usage.daily_requests == 2

    def test_cost_summary(self, acct_mgr, sample_account):
        acct_mgr.add_account(sample_account)
        acct_mgr.record_usage(sample_account.id, 1000, 500, 0.05)
        summary = acct_mgr.get_cost_summary()
        assert summary["daily_total_usd"] == pytest.approx(0.05)
        assert summary["accounts_active"] == 1


class TestBudgetEnforcement:
    def test_under_budget(self, sample_account):
        sample_account.usage.record(1000, 500, 1.0)
        assert sample_account.is_over_budget is False

    def test_over_daily_budget(self, sample_account):
        sample_account.usage.record(100_000, 50_000, 5.01)
        assert sample_account.is_over_budget is True

    def test_over_monthly_budget(self, sample_account):
        sample_account.usage.record(100_000, 50_000, 60.01)
        assert sample_account.is_over_budget is True

    def test_budget_remaining(self, sample_account):
        sample_account.usage.record(1000, 500, 2.0)
        remaining = sample_account.limits.budget_remaining(sample_account.usage)
        assert remaining["daily_remaining_usd"] == pytest.approx(3.0)
        assert remaining["monthly_remaining_usd"] == pytest.approx(58.0)

    def test_find_cheapest_active(self, acct_mgr):
        a1 = Account(
            name="Expensive",
            provider=ProviderType.ANTHROPIC,
            credentials=AccountCredentials(api_key="sk-1"),
            limits=AccountLimits(user_daily_budget_usd=2.0, user_monthly_budget_usd=30.0),
        )
        a1.usage.record(1000, 500, 1.8)  # almost at daily limit

        a2 = Account(
            name="Cheap",
            provider=ProviderType.ANTHROPIC,
            credentials=AccountCredentials(api_key="sk-2"),
            limits=AccountLimits(user_daily_budget_usd=5.0, user_monthly_budget_usd=60.0),
        )
        a2.usage.record(1000, 500, 0.5)  # plenty of budget

        acct_mgr.add_account(a1)
        acct_mgr.add_account(a2)

        best = acct_mgr.find_cheapest_active(ProviderType.ANTHROPIC)
        assert best is not None
        assert best.id == a2.id  # more remaining budget


class TestSerialization:
    def test_save_and_reload(self, tmp_path, sample_account):
        config_path = str(tmp_path / "accounts.json")

        # Save
        mgr1 = AccountManager(config_path=config_path)
        mgr1.add_account(sample_account)
        mgr1.record_usage(sample_account.id, 1000, 500, 0.05)
        binding = ToolBinding(
            tool_type=ToolType.WINDSURF,
            tool_name="Windsurf Work",
            vm_id="vm-ws-001",
        )
        mgr1.bind_tool(sample_account.id, binding)

        # Force save
        mgr1._save()

        # Reload from file
        mgr2 = AccountManager(config_path=config_path)
        assert len(mgr2.accounts) == 1
        reloaded = list(mgr2.accounts.values())[0]
        assert reloaded.name == "Anthropic Work"
        assert reloaded.provider == ProviderType.ANTHROPIC
        assert reloaded.usage.total_requests == 1
        assert reloaded.usage.total_cost_usd == pytest.approx(0.05)
        assert len(reloaded.tools) == 1
        assert reloaded.tools[0].tool_type == ToolType.WINDSURF

    def test_credentials_masked_in_summary(self, sample_account):
        summary = sample_account.status_summary()
        cred = summary["credentials"]
        # Key: "sk-ant-test1234567890abcdef" (26 chars)
        # Masked: first 8 + "..." + last 4 = "sk-ant-t...cdef"
        assert cred["api_key"].startswith("sk-ant-t")
        assert cred["api_key"].endswith("cdef")
        assert "..." in cred["api_key"]
        # Middle should not be visible
        assert "1234567890" not in cred["api_key"]

    def test_status_summary_structure(self, sample_account):
        summary = sample_account.status_summary()
        assert "id" in summary
        assert "name" in summary
        assert "provider" in summary
        assert "usage" in summary
        assert "budget" in summary
        assert "tools" in summary
