"""Tests for API007 Untyped Response Checker (free tier)."""

from pathlib import Path

from stablestack.checkers.api.untyped_response import UntypedResponseChecker


class TestAPI007:
    def setup_method(self):
        self.checker = UntypedResponseChecker()

    def test_metadata(self):
        assert self.checker.rule_id == "API007"
        assert self.checker.rule_name == "untyped-api-response"
        assert self.checker.is_project_checker is False

    # ── FastAPI ─────────────────────────────────────────────────────────

    def test_fastapi_missing_response_model(self, tmp_path):
        """FastAPI endpoint without response_model — finding."""
        p = tmp_path / "app.py"
        content = """\
from fastapi import FastAPI

app = FastAPI()

@app.get("/api/users")
async def get_users():
    return [{"name": "Alice"}]
"""
        p.write_text(content)
        findings = self.checker.check_file(p, content)
        assert len(findings) == 1
        assert "GET /api/users" in findings[0].message
        assert "no typed response" in findings[0].message

    def test_fastapi_with_response_model(self, tmp_path):
        """FastAPI endpoint with response_model — no finding."""
        p = tmp_path / "app.py"
        content = """\
from fastapi import FastAPI
from pydantic import BaseModel

class UserResponse(BaseModel):
    name: str

app = FastAPI()

@app.get("/api/users", response_model=UserResponse)
async def get_users() -> UserResponse:
    return UserResponse(name="Alice")
"""
        p.write_text(content)
        findings = self.checker.check_file(p, content)
        assert len(findings) == 0

    def test_fastapi_health_endpoint_skipped(self, tmp_path):
        """Health endpoints are not flagged."""
        p = tmp_path / "app.py"
        content = """\
from fastapi import FastAPI

app = FastAPI()

@app.get("/health")
async def health():
    return {"status": "ok"}
"""
        p.write_text(content)
        findings = self.checker.check_file(p, content)
        assert len(findings) == 0

    def test_fastapi_redirect_not_flagged(self, tmp_path):
        """Redirect responses don't need typing."""
        p = tmp_path / "app.py"
        content = """\
from fastapi import FastAPI
from fastapi.responses import RedirectResponse

app = FastAPI()

@app.get("/old-path",
         response_class=RedirectResponse)
async def redirect():
    return RedirectResponse("/new-path")
"""
        p.write_text(content)
        findings = self.checker.check_file(p, content)
        assert len(findings) == 0

    # ── Express ─────────────────────────────────────────────────────────

    def test_express_untyped_res_json(self, tmp_path):
        """Express endpoint without typed res.json — finding."""
        p = tmp_path / "server.ts"
        content = """\
import express from 'express';
const app = express();

app.get("/api/users", (req, res) => {
  res.json([{ name: "Alice" }]);
});
"""
        p.write_text(content)
        findings = self.checker.check_file(p, content)
        assert len(findings) == 1
        assert "GET /api/users" in findings[0].message

    def test_express_typed_res_json(self, tmp_path):
        """Express endpoint with res.json<Type>() — no finding."""
        p = tmp_path / "server.ts"
        content = """\
import express from 'express';
const app = express();

app.get("/api/users", (req, res) => {
  res.json<UserResponse[]>([{ name: "Alice" }]);
});
"""
        p.write_text(content)
        findings = self.checker.check_file(p, content)
        assert len(findings) == 0

    def test_express_health_skipped(self, tmp_path):
        """Express health endpoint — no finding."""
        p = tmp_path / "server.ts"
        content = """\
app.get("/health", (req, res) => {
  res.json({ status: "ok" });
});
"""
        p.write_text(content)
        findings = self.checker.check_file(p, content)
        assert len(findings) == 0

    # ── Next.js App Router ──────────────────────────────────────────────

    def test_nextjs_app_router_untyped(self, tmp_path):
        """Next.js App Router handler without typed NextResponse — finding."""
        route_dir = tmp_path / "app" / "api" / "users"
        route_dir.mkdir(parents=True)
        p = route_dir / "route.ts"
        content = """\
import { NextResponse } from 'next/server';

export async function GET(request: Request) {
  return NextResponse.json({ name: "Alice" });
}
"""
        p.write_text(content)
        findings = self.checker.check_file(p, content)
        assert len(findings) == 1
        assert "GET handler" in findings[0].message

    def test_nextjs_app_router_typed(self, tmp_path):
        """Next.js App Router handler with typed NextResponse — no finding."""
        route_dir = tmp_path / "app" / "api" / "users"
        route_dir.mkdir(parents=True)
        p = route_dir / "route.ts"
        content = """\
import { NextResponse } from 'next/server';

export async function GET(request: Request) {
  return NextResponse.json<UserResponse>({ name: "Alice" });
}
"""
        p.write_text(content)
        findings = self.checker.check_file(p, content)
        assert len(findings) == 0

    def test_multiple_endpoints_mixed(self, tmp_path):
        """Multiple endpoints, some typed some not — only untyped flagged."""
        p = tmp_path / "app.py"
        content = """\
from fastapi import FastAPI
from pydantic import BaseModel

class UserResponse(BaseModel):
    name: str

app = FastAPI()

@app.get("/api/users", response_model=UserResponse)
async def get_users() -> UserResponse:
    pass

@app.post("/api/users")
async def create_user():
    pass

@app.get("/api/items")
async def get_items():
    pass
"""
        p.write_text(content)
        findings = self.checker.check_file(p, content)
        assert len(findings) == 2
        paths = {f.message for f in findings}
        assert any("/api/users" in m and "POST" in m for m in paths)
        assert any("/api/items" in m and "GET" in m for m in paths)
