import json
import os
from dataclasses import dataclass
from enum import Enum
from typing import Any, Dict, Optional

import requests


class PolicyActions(str, Enum):
    INCREMENT_POLICY = "INCREMENT_POLICY"
    GET_POLICY_COUNT = "GET_POLICY_COUNT"
    GET_CURRENT_POLICY_COUNT = "GET_CURRENT_POLICY_COUNT"


class RuleStatus(str, Enum):
    PASS = "PASS"
    BLOCK = "BLOCK"


class FlowGuardError(Exception):
    """Generic error raised for Floowguard API failures."""

    def __init__(self, message: str, status: Optional[int] = None, body: Any = None):
        super().__init__(message)
        self.status = status
        self.body = body


@dataclass
class FlowGuardApiClient:
    """Minimal Python client mirroring the Node FlowGuardApiClient.

    It assumes application-origin requests and uses the same base URL and
    actions as the Node SDK.
    """

    application_id: str
    base_url: str = "https://server.floowguard.com/api"
    timeout: int = 10

    def __post_init__(self) -> None:
        if not self.application_id:
            raise ValueError("Application ID is required")

        # Allow overriding from environment if desired
        env_base = os.getenv("FLOOWGUARD_BASE_URL")
        if env_base:
            self.base_url = env_base.rstrip("/")

    # Public API -----------------------------------------------------------
    def increment_and_get_policy_count(self, *, policy_name: str, value: str) -> Dict[str, Any]:
        """Increment a policy counter and return the latest rule response."""

        payload = {
            "policyName": policy_name,
            "applicationId": self.application_id,
            "policyContextValue": value,
        }

        return self._post_policy({
            "action": PolicyActions.INCREMENT_POLICY.value,
            "payload": payload,
        })

    def increment_and_get_rule_count(self, *, policy_name: str, value: str) -> Dict[str, Any]:
        """Deprecated alias for increment_and_get_policy_count."""

        return self.increment_and_get_policy_count(policy_name=policy_name, value=value)

    def get_rule_count(self, policy_name: str) -> Dict[str, Any]:
        """Fetch the current aggregate count for a policy."""

        payload = {
            "policyName": policy_name,
            "applicationId": self.application_id,
        }

        return self._post_policy({
            "action": PolicyActions.GET_POLICY_COUNT.value,
            "payload": payload,
        })

    def get_current_count(self, *, policy_name: str, value: str) -> Dict[str, Any]:
        """Call GET_CURRENT_POLICY_COUNT to inspect the latest decision."""

        payload = {
            "policyName": policy_name,
            "applicationId": self.application_id,
            "policyContextValue": value,
        }

        try:
            res = requests.get(
                f"{self.base_url}/policy",
                params={
                    "action": PolicyActions.GET_CURRENT_POLICY_COUNT.value,
                    "payload": json.dumps(payload),
                },
                headers={
                    "application_id": self.application_id,
                    "request_origin": "application",
                },
                timeout=self.timeout,
            )
        except requests.RequestException as exc:
            raise FlowGuardError(str(exc)) from exc

        data = self._parse_response(res)
        return data

    def has_access(self, *, policy_name: str, value: str) -> bool:
        """Return True if the policy decision is PASS for this value."""

        result = self.get_current_count(policy_name=policy_name, value=value)
        status = (result.get("response") or {}).get("status")

        if not status:
            return False

        return status != RuleStatus.BLOCK.value

    # Internal helpers ----------------------------------------------------
    def _post_policy(self, request: Dict[str, Any]) -> Dict[str, Any]:
        body = {
            "application_id": self.application_id,
            **request,
        }

        try:
            res = requests.post(
                f"{self.base_url}/policy",
                json=body,
                headers={
                    "application_id": self.application_id,
                    "request_origin": "application",
                },
                timeout=self.timeout,
            )
        except requests.RequestException as exc:
            raise FlowGuardError(str(exc)) from exc

        data = self._parse_response(res)
        return data

    def _parse_response(self, res: requests.Response) -> Dict[str, Any]:
        try:
            data = res.json()
        except ValueError:
            raise FlowGuardError(
                f"Floowguard API request failed (HTTP {res.status_code})",
                status=res.status_code,
                body=res.text,
            )

        if "errorMessage" in data and data["errorMessage"]:
            raise FlowGuardError(
                data["errorMessage"], status=res.status_code, body=data
            )

        return data
