"""Tests for the get_ariba_pr_detail skill."""

from __future__ import annotations

import json
from importlib.util import module_from_spec, spec_from_file_location
from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest
import requests

from core.skills import SkillLoader


@pytest.fixture
def skills_dir() -> Path:
    """Return the skills directory path."""
    return Path(__file__).parent.parent / "skills"


@pytest.fixture
def skill_loader(skills_dir: Path) -> SkillLoader:
    """Create a SkillLoader instance."""
    return SkillLoader(skills_dir)


@pytest.fixture
def ariba_env_vars(monkeypatch):
    """Set up Ariba Reporting API environment variables for testing."""
    monkeypatch.setenv("ARIBA_OAUTH_URL", "https://test.ariba.com/oauth/token")
    monkeypatch.setenv("ARIBA_CLIENT_ID", "test_client")
    monkeypatch.setenv("ARIBA_CLIENT_SECRET", "test_secret")
    monkeypatch.setenv("ARIBA_API_KEY", "test_api_key")
    monkeypatch.setenv("ARIBA_BASE_URL", "https://test.ariba.com")
    monkeypatch.setenv("ARIBA_REALM", "test_realm")


def _load_handler_module():
    """Import get_ariba_pr_detail handler module."""
    handler_path = Path(__file__).parent.parent / ".chatixia" / "skills" / "get_ariba_pr_detail" / "handler.py"
    spec = spec_from_file_location("get_ariba_pr_detail_handler", handler_path)
    module = module_from_spec(spec)
    assert spec.loader is not None
    spec.loader.exec_module(module)
    return module


def _mock_response(
    status_code: int = 200,
    payload: dict | list | None = None,
    text: str = "",
) -> MagicMock:
    """Build a requests-like mock response."""
    resp = MagicMock()
    resp.status_code = status_code
    resp.text = text
    resp.json.return_value = payload if payload is not None else {}
    if status_code >= 400:
        err = requests.exceptions.HTTPError(f"HTTP {status_code}")
        err.response = resp
        resp.raise_for_status.side_effect = err
    else:
        resp.raise_for_status.return_value = None
    return resp


class TestGetAribaPrDetailSkillLoading:
    """Tests for loading the get_ariba_pr_detail skill."""

    def test_skill_loads_successfully(self, skill_loader: SkillLoader) -> None:
        """Verify the skill loads without errors."""
        skills = skill_loader.load_all()
        assert "get_ariba_pr_detail" in skills

    def test_skill_has_handler(self, skill_loader: SkillLoader) -> None:
        """Verify the skill has a handler function."""
        skills = skill_loader.load_all()
        skill = skills["get_ariba_pr_detail"]
        assert skill.has_handler

    def test_skill_requires_pr_number(self, skill_loader: SkillLoader) -> None:
        """Verify pr_number is marked required in metadata."""
        skills = skill_loader.load_all()
        skill = skills["get_ariba_pr_detail"]
        required_params = {p.name for p in skill.parameters if p.required}
        assert "pr_number" in required_params


class TestGetAribaPrDetailHandler:
    """Tests for get_ariba_pr_detail handler behavior."""

    def test_handler_missing_config(self, monkeypatch) -> None:
        """Verify missing environment variables are reported."""
        module = _load_handler_module()

        for env_name in (
            "ARIBA_OAUTH_URL",
            "ARIBA_CLIENT_ID",
            "ARIBA_CLIENT_SECRET",
            "ARIBA_API_KEY",
            "ARIBA_BASE_URL",
            "ARIBA_REALM",
        ):
            monkeypatch.delenv(env_name, raising=False)

        result = module.handle(pr_number="PR66729")
        data = json.loads(result)
        assert data["ok"] is False
        assert "Missing environment variables" in data["error"]

    def test_returns_detail_with_line_items(self, ariba_env_vars) -> None:
        """Verify the handler returns PR header and line items when available."""
        module = _load_handler_module()

        with patch.object(module.requests, "post") as mock_post, patch.object(module.requests, "get") as mock_get:
            mock_post.return_value = _mock_response(payload={"access_token": "test_token"})

            def get_side_effect(url, **kwargs):
                if url.endswith("/viewTemplates"):
                    return _mock_response(
                        payload={
                            "Records": [
                                {"Name": "Requisition_Generic_updatedRange"},
                                {"Name": "Requisition_LineItems_updatedRange"},
                            ]
                        }
                    )
                if "/views/Requisition_LineItems_updatedRange" in url:
                    return _mock_response(
                        payload={
                            "Records": [
                                {
                                    "UniqueName": "PR66729",
                                    "Name": "Detail Test PR",
                                    "StatusString": "Composing",
                                    "Requester": {"UniqueName": "100136678", "Name": {}},
                                    "Preparer": {"UniqueName": "100136678", "Name": {}},
                                    "TotalCost": {
                                        "Amount": 1179200,
                                        "Currency": {"UniqueName": "JPY"},
                                    },
                                    "TimeCreated": "2026-01-21T23:57:41Z",
                                    "TimeUpdated": "2026-01-27T09:40:19Z",
                                    "LineItems": {
                                        "item": [
                                            {
                                                "LineNumber": 1,
                                                "Description": {
                                                    "Description": "Line item A",
                                                    "Price": {
                                                        "Amount": 1000,
                                                        "Currency": {"UniqueName": "JPY"},
                                                    },
                                                },
                                                "Quantity": 2,
                                                "Supplier": {"UniqueName": "0076112"},
                                                "SupplierLocation": {"UniqueName": "0076112A01"},
                                                "ImportedDeliverToStaging": "Tokyo Site",
                                                "ImportedNeedByStaging": "2026-02-01T00:00:00Z",
                                                "ImportedLineCommentStaging": "Urgent",
                                            }
                                        ]
                                    },
                                }
                            ]
                        }
                    )
                return _mock_response(payload={"Records": []})

            mock_get.side_effect = get_side_effect

            result = module.handle(pr_number="PR66729")
            data = json.loads(result)

            assert data["ok"] is True
            assert data["source_view"] == "Requisition_LineItems_updatedRange"
            assert data["detail"]["header"]["pr_number"] == "PR66729"
            assert data["detail"]["line_item_count"] == 1
            assert data["detail"]["line_items"][0]["description"] == "Line item A"
            assert data["detail"]["line_items"][0]["supplier"] == "0076112"

    def test_not_found_returns_attempted_views(self, ariba_env_vars) -> None:
        """Verify no-match response includes attempted views and suggestion."""
        module = _load_handler_module()

        with patch.object(module.requests, "post") as mock_post, patch.object(module.requests, "get") as mock_get:
            mock_post.return_value = _mock_response(payload={"access_token": "test_token"})

            def get_side_effect(url, **kwargs):
                if url.endswith("/viewTemplates"):
                    return _mock_response(
                        payload={
                            "Records": [
                                {"Name": "Requisition_Detail_updatedRange"},
                                {"Name": "Requisition_Generic_updatedRange"},
                            ]
                        }
                    )
                return _mock_response(
                    payload={
                        "Records": [
                            {
                                "UniqueName": "PR00001",
                                "Name": "Other PR",
                                "StatusString": "Submitted",
                            }
                        ]
                    }
                )

            mock_get.side_effect = get_side_effect

            result = module.handle(pr_number="PR99999")
            data = json.loads(result)

            assert data["ok"] is False
            assert "attempted_views" in data
            assert len(data["attempted_views"]) >= 1
            assert "suggestion" in data

    def test_rate_limited_run_is_marked_incomplete(self, ariba_env_vars) -> None:
        """Verify repeated HTTP 429 is surfaced as incomplete search."""
        module = _load_handler_module()

        with patch.object(module.requests, "post") as mock_post, patch.object(module.requests, "get") as mock_get:
            mock_post.return_value = _mock_response(payload={"access_token": "test_token"})
            mock_get.return_value = _mock_response(status_code=429, text="Too Many Requests")

            result = module.handle(
                pr_number="PR66729",
                max_views=2,
                max_retries_per_view=0,
                request_interval_sec=0,
            )
            data = json.loads(result)

            assert data["ok"] is False
            assert data["search_complete"] is False
            assert data["rate_limited_views"] >= 1
            assert "rate limit" in data["error"].lower() or "429" in data["error"]
