"""Tests for STRUCT013: Duplicate Client Initialization checker."""

import pytest
from pathlib import Path

from stablestack.checkers.structure.duplicate_client_init import DuplicateClientInitChecker


@pytest.fixture
def checker():
    return DuplicateClientInitChecker()


def make_file(tmp_path: Path, name: str, content: str) -> Path:
    """Create a file in the tmp directory."""
    p = tmp_path / name
    p.parent.mkdir(parents=True, exist_ok=True)
    p.write_text(content)
    return p


class TestDuplicateClientInit:
    """Test that duplicate SDK client instantiations across files are detected."""

    def test_flags_duplicate_resend_init(self, checker: DuplicateClientInitChecker, tmp_path: Path):
        """The exact bug: new Resend() in helpers.ts AND ses.ts."""
        make_file(tmp_path, "helpers.ts", '''
import { Resend } from "resend";
let client: Resend | null = null;
export function getResend(): Resend | null {
  if (!client) {
    client = new Resend(process.env.RESEND_API_KEY);
  }
  return client;
}
''')
        make_file(tmp_path, "ses.ts", '''
import { Resend } from "resend";
let resendClient: Resend | null = null;
function getResend(): Resend | null {
  if (!resendClient) {
    resendClient = new Resend(process.env.RESEND_API_KEY);
  }
  return resendClient;
}
''')

        findings = checker.check_project(tmp_path)

        assert len(findings) == 2  # One per file
        assert all(f.rule_id == "STRUCT013" for f in findings)
        assert any("helpers.ts" in f.message for f in findings)
        assert any("ses.ts" in f.message for f in findings)
        assert all("resend" in f.message for f in findings)

    def test_no_flag_single_file(self, checker: DuplicateClientInitChecker, tmp_path: Path):
        """Only one file has new Resend() — no finding."""
        make_file(tmp_path, "helpers.ts", '''
import { Resend } from "resend";
const client = new Resend(process.env.RESEND_API_KEY);
''')
        make_file(tmp_path, "sender.ts", '''
import { getResend } from "./helpers";
const resend = getResend();
''')

        findings = checker.check_project(tmp_path)
        assert len(findings) == 0

    def test_ignores_builtin_classes(self, checker: DuplicateClientInitChecker, tmp_path: Path):
        """new Date(), new Error(), new Map() should never be flagged."""
        make_file(tmp_path, "utils.ts", '''
const now = new Date();
const err = new Error("oops");
const map = new Map<string, number>();
''')
        make_file(tmp_path, "helpers.ts", '''
const later = new Date();
const e = new Error("bad");
const m = new Map<string, string>();
''')

        findings = checker.check_project(tmp_path)
        assert len(findings) == 0

    def test_ignores_relative_imports(self, checker: DuplicateClientInitChecker, tmp_path: Path):
        """Classes imported from relative paths (./local) should not be flagged."""
        make_file(tmp_path, "a.ts", '''
import { MyClient } from "./client";
const c = new MyClient();
''')
        make_file(tmp_path, "b.ts", '''
import { MyClient } from "../client";
const c = new MyClient();
''')

        findings = checker.check_project(tmp_path)
        assert len(findings) == 0

    def test_flags_stripe_duplicate(self, checker: DuplicateClientInitChecker, tmp_path: Path):
        """Another common SDK: Stripe."""
        make_file(tmp_path, "billing.ts", '''
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_KEY);
''')
        make_file(tmp_path, "webhooks.ts", '''
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_KEY);
''')

        findings = checker.check_project(tmp_path)

        assert len(findings) == 2
        assert all("Stripe" in f.message for f in findings)

    def test_skips_test_files(self, checker: DuplicateClientInitChecker, tmp_path: Path):
        """Test files may intentionally duplicate init for mocking."""
        make_file(tmp_path, "helpers.ts", '''
import { Resend } from "resend";
const client = new Resend(process.env.RESEND_API_KEY);
''')
        make_file(tmp_path, "helpers.test.ts", '''
import { Resend } from "resend";
const mockClient = new Resend("test-key");
''')

        findings = checker.check_project(tmp_path)
        assert len(findings) == 0

    def test_noqa_suppresses(self, checker: DuplicateClientInitChecker, tmp_path: Path):
        """Lines with // noqa should be skipped."""
        make_file(tmp_path, "helpers.ts", '''
import { Resend } from "resend";
const client = new Resend(process.env.RESEND_API_KEY);
''')
        make_file(tmp_path, "legacy.ts", '''
import { Resend } from "resend";
const client = new Resend(process.env.RESEND_API_KEY); // noqa
''')

        findings = checker.check_project(tmp_path)
        assert len(findings) == 0

    def test_three_files_flags_all(self, checker: DuplicateClientInitChecker, tmp_path: Path):
        """Three files with same init — all three should be flagged."""
        for name in ["a.ts", "b.ts", "c.ts"]:
            make_file(tmp_path, name, '''
import { Resend } from "resend";
const r = new Resend("key");
''')

        findings = checker.check_project(tmp_path)

        assert len(findings) == 3
        file_names = {Path(f.file).name for f in findings}
        assert file_names == {"a.ts", "b.ts", "c.ts"}

    def test_flags_prisma_client(self, checker: DuplicateClientInitChecker, tmp_path: Path):
        """PrismaClient is a common one to accidentally duplicate."""
        make_file(tmp_path, "db.ts", '''
import { PrismaClient } from "@prisma/client";
export const db = new PrismaClient();
''')
        make_file(tmp_path, "seed.ts", '''
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
''')

        findings = checker.check_project(tmp_path)

        assert len(findings) == 2
        assert all("PrismaClient" in f.message for f in findings)

    def test_default_import_style(self, checker: DuplicateClientInitChecker, tmp_path: Path):
        """Default imports (import X from "pkg") should also be tracked."""
        make_file(tmp_path, "pay.ts", '''
import Stripe from "stripe";
const s = new Stripe("sk_test");
''')
        make_file(tmp_path, "refund.ts", '''
import Stripe from "stripe";
const s = new Stripe("sk_test");
''')

        findings = checker.check_project(tmp_path)
        assert len(findings) == 2

    def test_skips_e2e_directory(self, checker: DuplicateClientInitChecker, tmp_path: Path):
        """Files in e2e/ should be skipped."""
        make_file(tmp_path, "helpers.ts", '''
import { Resend } from "resend";
const client = new Resend(process.env.RESEND_API_KEY);
''')
        make_file(tmp_path, "e2e/setup.ts", '''
import { Resend } from "resend";
const mock = new Resend("test");
''')

        findings = checker.check_project(tmp_path)
        assert len(findings) == 0

    def test_is_project_checker(self, checker: DuplicateClientInitChecker):
        """Verify this is registered as a project-level checker."""
        assert checker.is_project_checker is True
