"""Tests for PERF003 (redundant-trpc-calls) and PERF004 (subscription-waterfall)."""

import pytest
from pathlib import Path

from stablestack.checkers.performance.redundant_trpc_calls import RedundantTrpcCallsChecker
from stablestack.checkers.performance.subscription_waterfall import SubscriptionWaterfallChecker


# ── Local fixtures ──

@pytest.fixture
def tsx_file(tmp_path: Path):
    def _create(content: str, name: str = "Dashboard.tsx") -> Path:
        p = tmp_path / name
        p.write_text(content)
        return p
    return _create


@pytest.fixture
def ts_file(tmp_path: Path):
    def _create(content: str, name: str = "service.ts") -> Path:
        p = tmp_path / name
        p.write_text(content)
        return p
    return _create


# ============================================================
# PERF003: redundant-trpc-calls
# ============================================================


class TestRedundantTrpcCalls:
    @pytest.fixture
    def checker(self):
        return RedundantTrpcCallsChecker()

    # --- Detection ---

    def test_detects_3_calls_same_router(self, checker, tsx_file):
        code = """\
export function TeamPage() {
  const { data: a } = trpc.team.getMembers.useQuery();
  const { data: b } = trpc.team.getSettings.useQuery();
  const { data: c } = trpc.team.getPermissions.useQuery();
  return <div />;
}
"""
        findings = checker.check_file(tsx_file(code), code)
        assert len(findings) == 1
        assert findings[0].rule_id == "PERF003"
        assert "team" in findings[0].message

    def test_detects_4_calls_same_router(self, checker, tsx_file):
        code = """\
const { data: a } = trpc.billing.getInvoices.useQuery();
const { data: b } = trpc.billing.getSubscription.useQuery();
const { data: c } = trpc.billing.getPaymentMethod.useQuery();
const { data: d } = trpc.billing.getUsage.useQuery();
"""
        findings = checker.check_file(tsx_file(code), code)
        assert len(findings) == 1
        assert "4" in findings[0].message

    # --- Suppression ---

    def test_no_flag_different_routers(self, checker, tsx_file):
        code = """\
const { data: a } = trpc.team.getMembers.useQuery();
const { data: b } = trpc.billing.getInvoices.useQuery();
const { data: c } = trpc.user.getProfile.useQuery();
"""
        findings = checker.check_file(tsx_file(code), code)
        assert len(findings) == 0

    def test_no_flag_fewer_than_3(self, checker, tsx_file):
        code = """\
const { data: a } = trpc.team.getMembers.useQuery();
const { data: b } = trpc.team.getSettings.useQuery();
"""
        findings = checker.check_file(tsx_file(code), code)
        assert len(findings) == 0

    def test_skips_test_files(self, checker, tmp_path):
        code = """\
const { data: a } = trpc.team.getMembers.useQuery();
const { data: b } = trpc.team.getSettings.useQuery();
const { data: c } = trpc.team.getPermissions.useQuery();
"""
        p = tmp_path / "team.test.tsx"
        p.write_text(code)
        findings = checker.check_file(p, code)
        assert len(findings) == 0

    # --- Edge cases ---

    def test_empty_file(self, checker, tsx_file):
        findings = checker.check_file(tsx_file(""), "")
        assert len(findings) == 0

    def test_multiple_routers_both_flagged(self, checker, tsx_file):
        """Two different routers each with 3+ calls should produce 2 findings."""
        code = """\
const { data: a } = trpc.team.getMembers.useQuery();
const { data: b } = trpc.team.getSettings.useQuery();
const { data: c } = trpc.team.getPermissions.useQuery();
const { data: d } = trpc.billing.getInvoices.useQuery();
const { data: e } = trpc.billing.getSubscription.useQuery();
const { data: f } = trpc.billing.getUsage.useQuery();
"""
        findings = checker.check_file(tsx_file(code), code)
        assert len(findings) == 2

    def test_no_trpc_calls(self, checker, tsx_file):
        code = """\
const { data } = useQuery({ queryKey: ['users'], queryFn: fetchUsers });
"""
        findings = checker.check_file(tsx_file(code), code)
        assert len(findings) == 0


# ============================================================
# PERF004: subscription-waterfall
# ============================================================


class TestSubscriptionWaterfall:
    @pytest.fixture
    def checker(self):
        return SubscriptionWaterfallChecker()

    # --- Detection ---

    def test_detects_waterfall_2_children(self, checker, tsx_file):
        code = """\
export function SubscriptionPage() {
  const { data: sub } = trpc.subscription.get.useQuery();
  const { data: features } = trpc.features.list.useQuery(
    { planId: sub?.planId },
    { enabled: !!sub }
  );
  const { data: limits } = trpc.limits.get.useQuery(
    { planId: sub?.planId },
    { enabled: !!sub }
  );
  return <div />;
}
"""
        findings = checker.check_file(tsx_file(code), code)
        assert len(findings) == 1
        assert findings[0].rule_id == "PERF004"
        assert "sub" in findings[0].message

    def test_detects_waterfall_3_children(self, checker, tsx_file):
        code = """\
export function Page() {
  const { data: user } = useQuery({ queryKey: ['user'], queryFn: fetchUser });
  const { data: posts } = useQuery({ queryKey: ['posts'], queryFn: fetchPosts, enabled: !!user });
  const { data: comments } = useQuery({ queryKey: ['comments'], queryFn: fetchComments, enabled: !!user });
  const { data: likes } = useQuery({ queryKey: ['likes'], queryFn: fetchLikes, enabled: !!user });
  return <div />;
}
"""
        findings = checker.check_file(tsx_file(code), code)
        assert len(findings) == 1
        assert "3" in findings[0].message

    # --- Suppression ---

    def test_no_flag_single_child(self, checker, tsx_file):
        """One child depending on parent is a normal dependent query."""
        code = """\
export function Page() {
  const { data: user } = useQuery({ queryKey: ['user'], queryFn: fetchUser });
  const { data: posts } = useQuery({ queryKey: ['posts'], queryFn: fetchPosts, enabled: !!user });
  return <div />;
}
"""
        findings = checker.check_file(tsx_file(code), code)
        assert len(findings) == 0

    def test_no_flag_without_enabled(self, checker, tsx_file):
        code = """\
export function Page() {
  const { data: a } = useQuery({ queryKey: ['a'], queryFn: fetchA });
  const { data: b } = useQuery({ queryKey: ['b'], queryFn: fetchB });
  return <div />;
}
"""
        findings = checker.check_file(tsx_file(code), code)
        assert len(findings) == 0

    def test_skips_test_files(self, checker, tmp_path):
        code = """\
const { data: sub } = trpc.subscription.get.useQuery();
const { data: a } = trpc.a.get.useQuery({ enabled: !!sub });
const { data: b } = trpc.b.get.useQuery({ enabled: !!sub });
"""
        p = tmp_path / "sub.test.tsx"
        p.write_text(code)
        findings = checker.check_file(p, code)
        assert len(findings) == 0

    # --- Edge cases ---

    def test_empty_file(self, checker, tsx_file):
        findings = checker.check_file(tsx_file(""), "")
        assert len(findings) == 0

    def test_aliased_data(self, checker, tsx_file):
        """Should detect when data is aliased (data: subscription)."""
        code = """\
export function Page() {
  const { data: subscription } = trpc.sub.get.useQuery();
  const { data: features } = trpc.features.list.useQuery({ enabled: !!subscription });
  const { data: limits } = trpc.limits.get.useQuery({ enabled: !!subscription });
  return <div />;
}
"""
        findings = checker.check_file(tsx_file(code), code)
        assert len(findings) == 1
        assert "subscription" in findings[0].message

    def test_boolean_enabled_not_data_var(self, checker, tsx_file):
        """enabled: !!someFlag that is NOT from a query should not flag."""
        code = """\
export function Page() {
  const isAdmin = useContext(AuthContext);
  const { data: a } = useQuery({ queryKey: ['a'], enabled: !!isAdmin });
  const { data: b } = useQuery({ queryKey: ['b'], enabled: !!isAdmin });
  return <div />;
}
"""
        findings = checker.check_file(tsx_file(code), code)
        assert len(findings) == 0
