import json
import os
import sys
from pathlib import Path

import jsonschema
import pytest

sys.path.insert(0, str(Path(__file__).resolve().parent.parent))

from adapters.wiz.wiz_adapter import (
    SEVERITY_MAP, RESULT_MAP, _to_pcr, _emit_not_configured, adapt, is_configured,
)


FIXTURES = Path(__file__).resolve().parent.parent / "tests" / "fixtures"


class TestSeverityResultMaps:
    def test_severity_map_critical(self):
        assert SEVERITY_MAP["CRITICAL"] == "critical"
        assert SEVERITY_MAP["HIGH"] == "high"
        assert SEVERITY_MAP["MEDIUM"] == "medium"
        assert SEVERITY_MAP["LOW"] == "low"
        assert SEVERITY_MAP["INFO"] == "info"

    def test_result_map_open_is_fail(self):
        assert RESULT_MAP["OPEN"] == "fail"
        assert RESULT_MAP["RESOLVED"] == "pass"
        assert RESULT_MAP["IN_PROGRESS"] == "skipped"
        assert RESULT_MAP["DISMISSED"] == "skipped"


class TestToPcr:
    def test_translates_open_critical(self):
        issue = {
            "id": "wiz-1",
            "title": "a critical issue",
            "severity": "CRITICAL",
            "status": "OPEN",
            "entity": {"id": "arn:aws:s3:::b", "name": "b"},
        }
        pcr = _to_pcr(issue, "c-1")
        assert pcr["engine"] == "wiz"
        assert pcr["ruleId"] == "wiz-1"
        assert pcr["severity"] == "critical"
        assert pcr["result"] == "fail"
        assert pcr["contractId"] == "c-1"
        assert pcr["resourceRef"] == "arn:aws:s3:::b"

    def test_severity_case_insensitive(self):
        issue = {"id": "wiz-1", "severity": "high", "status": "open",
                 "entity": {"id": "r"}}
        pcr = _to_pcr(issue, "c-1")
        assert pcr["severity"] == "high"
        assert pcr["result"] == "fail"

    def test_unknown_severity_defaults_info(self):
        issue = {"id": "wiz-1", "severity": "BOGUS", "status": "OPEN",
                 "entity": {"id": "r"}}
        pcr = _to_pcr(issue, "c-1")
        assert pcr["severity"] == "info"

    def test_unknown_status_defaults_error(self):
        issue = {"id": "wiz-1", "severity": "INFO", "status": "BOGUS",
                 "entity": {"id": "r"}}
        pcr = _to_pcr(issue, "c-1")
        assert pcr["result"] == "error"

    def test_pcr_validates_against_schema(self, policy_check_result_schema):
        issue = {"id": "wiz-1", "title": "t", "severity": "CRITICAL",
                 "status": "OPEN", "entity": {"id": "r", "name": "n"}}
        pcr = _to_pcr(issue, "11111111-1111-1111-1111-111111111111")
        jsonschema.validate(pcr, policy_check_result_schema)


class TestEmitNotConfigured:
    def test_not_configured_pcr(self, policy_check_result_schema):
        pcr = _emit_not_configured("11111111-1111-1111-1111-111111111111")
        assert pcr["ruleId"] == "WIZ_NOT_CONFIGURED"
        assert pcr["result"] == "skipped"
        assert pcr["engine"] == "wiz"
        jsonschema.validate(pcr, policy_check_result_schema)


class TestAdapt:
    def test_translates_fixture(self, tmp_path, policy_check_result_schema):
        # adapt() reads a file path, copy fixture to a writable temp path
        src = FIXTURES / "wiz_issues.json"
        f = tmp_path / "wiz_issues.json"
        f.write_text(src.read_text())
        results = adapt(str(f), "11111111-1111-1111-1111-111111111111")
        assert len(results) == 3

        # issue 1: OPEN critical -> fail/critical; ruleId = control.name (v1.9 real client)
        assert results[0]["ruleId"] == "Public S3 bucket exposure"
        assert results[0]["severity"] == "critical"
        assert results[0]["result"] == "fail"

        # issue 2: RESOLVED high -> pass/high
        assert results[1]["ruleId"] == "Overly broad IAM role"
        assert results[1]["severity"] == "high"
        assert results[1]["result"] == "pass"

        # issue 3: IN_PROGRESS medium -> skipped/medium
        assert results[2]["ruleId"] == "SSH open to the world"
        assert results[2]["severity"] == "medium"
        assert results[2]["result"] == "skipped"

        for pcr in results:
            jsonschema.validate(pcr, policy_check_result_schema)

    def test_empty_issues_emits_not_configured(self, tmp_path):
        f = tmp_path / "wiz_empty.json"
        f.write_text(json.dumps({"issues": []}))
        results = adapt(str(f), "11111111-1111-1111-1111-111111111111")
        assert len(results) == 1
        assert results[0]["ruleId"] == "WIZ_NOT_CONFIGURED"
        assert results[0]["result"] == "skipped"

    def test_top_level_list_input(self, tmp_path):
        # data is a bare list (no "issues" wrapper)
        f = tmp_path / "wiz_list.json"
        f.write_text(json.dumps([
            {"id": "w-1", "severity": "LOW", "status": "RESOLVED",
             "entity": {"id": "r"}},
        ]))
        results = adapt(str(f), "11111111-1111-1111-1111-111111111111")
        assert len(results) == 1
        assert results[0]["severity"] == "low"
        assert results[0]["result"] == "pass"


class TestIsConfigured:
    def test_not_configured_when_env_unset(self, monkeypatch):
        monkeypatch.delenv("WIZ_API_TOKEN", raising=False)
        assert is_configured() is False

    def test_configured_when_env_set(self, monkeypatch):
        monkeypatch.setenv("WIZ_API_TOKEN", "token-abc")
        monkeypatch.setenv("WIZ_API_URL", "https://api.wiz.io")
        assert is_configured() is True