"""Tests for Vanta GRC evidence exporter."""

from __future__ import annotations

import json
from typing import TYPE_CHECKING
from unittest.mock import AsyncMock, patch

import httpx
import pytest

from complyform.core.errors import (
    ExportAPIAuthError,
    ExportAPIPushError,
    ExportAPIRateLimitError,
)
from complyform.core.generators.evidence_export.vanta import VantaExporter

if TYPE_CHECKING:
    from pathlib import Path


@pytest.fixture
def exporter() -> VantaExporter:
    return VantaExporter()


@pytest.fixture
def metadata() -> dict[str, object]:
    return {"scan_id": "abc12345", "timestamp": "2024-01-01T00:00:00Z", "cloud": "aws"}


@pytest.fixture
def sample_findings() -> list[dict[str, object]]:
    return [
        {
            "control_id": "CC6.1",
            "framework": "soc2",
            "resource_address": "aws_s3_bucket.example",
            "resource_type": "aws_s3_bucket",
            "status": "FAIL",
            "severity": "HIGH",
            "finding": "Bucket encryption not enabled",
            "remediation": "Enable SSE",
        },
        {
            "control_id": "CC7.2",
            "framework": "soc2",
            "resource_address": "aws_s3_bucket.logs",
            "resource_type": "aws_s3_bucket",
            "status": "PASS",
            "severity": "MEDIUM",
            "finding": "Logging enabled",
            "remediation": "",
        },
    ]


@pytest.fixture
def api_config() -> dict[str, object]:
    return {"client_id": "test-client-id", "client_secret": "test-client-secret"}


class TestVantaExporter:
    def test_export_to_file(
        self,
        exporter: VantaExporter,
        sample_findings: list[dict[str, object]],
        metadata: dict[str, object],
        tmp_path: Path,
    ) -> None:
        result = exporter.export_to_file(sample_findings, metadata, tmp_path)
        assert result.format == "vanta"
        assert result.record_count == 2
        assert len(result.output_files) == 1

        data = json.loads(result.output_files[0].read_text())
        assert data["platform"] == "vanta"
        assert data["document_count"] == 2
        assert len(data["documents"]) == 2
        assert data["documents"][0]["status"] == "FAIL"
        assert data["documents"][1]["status"] == "PASS"

    @pytest.mark.asyncio
    async def test_push_to_api_success(
        self,
        exporter: VantaExporter,
        sample_findings: list[dict[str, object]],
        metadata: dict[str, object],
        api_config: dict[str, object],
        tmp_path: Path,
    ) -> None:
        token_response = httpx.Response(
            200,
            json={"access_token": "test-token", "token_type": "bearer"},
            request=httpx.Request("POST", "https://api.vanta.com/oauth/token"),
        )
        upload_response = httpx.Response(
            200,
            json={"id": "doc-1"},
            request=httpx.Request(
                "POST", "https://api.vanta.com/v1/evidence/documents"
            ),
        )

        with (
            patch(
                "complyform.core.generators.evidence_export.vanta.grc_request",
                new_callable=AsyncMock,
                side_effect=[token_response, upload_response, upload_response],
            ),
            patch(
                "complyform.core.generators.evidence_export.vanta._default_output_dir",
                return_value=tmp_path,
            ),
        ):
            result = await exporter.push_to_api(sample_findings, metadata, api_config)
            assert result.api_push_status == "success"
            assert result.record_count == 2

    @pytest.mark.asyncio
    async def test_push_to_api_auth_failure(
        self,
        exporter: VantaExporter,
        sample_findings: list[dict[str, object]],
        metadata: dict[str, object],
        api_config: dict[str, object],
        tmp_path: Path,
    ) -> None:
        with (
            patch(
                "complyform.core.generators.evidence_export.vanta.grc_request",
                new_callable=AsyncMock,
                side_effect=ExportAPIAuthError("vanta"),
            ),
            patch(
                "complyform.core.generators.evidence_export.vanta._default_output_dir",
                return_value=tmp_path,
            ),
            pytest.raises(ExportAPIAuthError),
        ):
            await exporter.push_to_api(sample_findings, metadata, api_config)

    @pytest.mark.asyncio
    async def test_push_to_api_rate_limit(
        self,
        exporter: VantaExporter,
        sample_findings: list[dict[str, object]],
        metadata: dict[str, object],
        api_config: dict[str, object],
        tmp_path: Path,
    ) -> None:
        token_response = httpx.Response(
            200,
            json={"access_token": "test-token"},
            request=httpx.Request("POST", "https://api.vanta.com/oauth/token"),
        )

        with (
            patch(
                "complyform.core.generators.evidence_export.vanta.grc_request",
                new_callable=AsyncMock,
                side_effect=[
                    token_response,
                    ExportAPIRateLimitError("vanta"),
                ],
            ),
            patch(
                "complyform.core.generators.evidence_export.vanta._default_output_dir",
                return_value=tmp_path,
            ),
            pytest.raises(ExportAPIPushError),
        ):
            await exporter.push_to_api(sample_findings, metadata, api_config)

    @pytest.mark.asyncio
    async def test_push_to_api_server_error(
        self,
        exporter: VantaExporter,
        sample_findings: list[dict[str, object]],
        metadata: dict[str, object],
        api_config: dict[str, object],
        tmp_path: Path,
    ) -> None:
        from complyform.core.errors import ExportAPIError

        token_response = httpx.Response(
            200,
            json={"access_token": "test-token"},
            request=httpx.Request("POST", "https://api.vanta.com/oauth/token"),
        )

        with (
            patch(
                "complyform.core.generators.evidence_export.vanta.grc_request",
                new_callable=AsyncMock,
                side_effect=[
                    token_response,
                    ExportAPIError("vanta", 500, "Internal Server Error"),
                    ExportAPIError("vanta", 500, "Internal Server Error"),
                ],
            ),
            patch(
                "complyform.core.generators.evidence_export.vanta._default_output_dir",
                return_value=tmp_path,
            ),
            pytest.raises(ExportAPIPushError),
        ):
            await exporter.push_to_api(sample_findings, metadata, api_config)

    @pytest.mark.asyncio
    async def test_push_to_api_partial_failure(
        self,
        exporter: VantaExporter,
        sample_findings: list[dict[str, object]],
        metadata: dict[str, object],
        api_config: dict[str, object],
        tmp_path: Path,
    ) -> None:
        from complyform.core.errors import ExportAPIError

        token_response = httpx.Response(
            200,
            json={"access_token": "test-token"},
            request=httpx.Request("POST", "https://api.vanta.com/oauth/token"),
        )
        upload_success = httpx.Response(
            200,
            json={"id": "doc-1"},
            request=httpx.Request(
                "POST", "https://api.vanta.com/v1/evidence/documents"
            ),
        )

        with (
            patch(
                "complyform.core.generators.evidence_export.vanta.grc_request",
                new_callable=AsyncMock,
                side_effect=[
                    token_response,
                    upload_success,
                    ExportAPIError("vanta", 500, "fail"),
                ],
            ),
            patch(
                "complyform.core.generators.evidence_export.vanta._default_output_dir",
                return_value=tmp_path,
            ),
        ):
            result = await exporter.push_to_api(sample_findings, metadata, api_config)
            assert result.api_push_status == "partial"
            assert result.record_count == 1
            assert len(result.warnings) > 0
