"""
Billing Service Client fuer BreakPilot
Kommuniziert mit dem Billing Service fuer Subscription Management und Usage Tracking
"""

import httpx
from datetime import datetime
from typing import Optional, List, Dict, Any
from dataclasses import dataclass, field
from enum import Enum
import os

# Billing Service URL (aus Umgebungsvariable oder Standard fuer lokale Entwicklung)
BILLING_SERVICE_URL = os.getenv("BILLING_SERVICE_URL", "http://localhost:8083")


class PlanID(str, Enum):
    """Verfuegbare Plan-IDs"""
    BASIC = "basic"
    STANDARD = "standard"
    PREMIUM = "premium"


class SubscriptionStatus(str, Enum):
    """Subscription Status Werte"""
    TRIALING = "trialing"
    ACTIVE = "active"
    PAST_DUE = "past_due"
    CANCELED = "canceled"
    EXPIRED = "expired"


class TaskType(str, Enum):
    """Task-Typen fuer Usage Tracking"""
    CORRECTION = "correction"
    LETTER = "letter"
    MEETING = "meeting"
    BATCH = "batch"
    OTHER = "other"


@dataclass
class PlanFeatures:
    """Features eines Billing Plans"""
    monthly_task_allowance: int = 0
    max_task_balance: int = 0
    feature_flags: List[str] = field(default_factory=list)
    max_team_members: int = 1
    priority_support: bool = False
    custom_branding: bool = False
    batch_processing: bool = False
    custom_templates: bool = False
    fair_use_mode: bool = False


@dataclass
class BillingPlan:
    """Billing Plan Informationen"""
    id: PlanID
    name: str
    description: str
    price_cents: int
    currency: str
    interval: str
    features: PlanFeatures
    is_active: bool = True
    sort_order: int = 0


@dataclass
class SubscriptionInfo:
    """Subscription Details"""
    plan_id: PlanID
    plan_name: str
    status: SubscriptionStatus
    is_trialing: bool
    cancel_at_period_end: bool
    price_cents: int
    currency: str
    trial_days_left: int = 0
    current_period_end: Optional[str] = None


@dataclass
class TaskUsageInfo:
    """Task-basierte Usage Informationen"""
    tasks_available: int
    max_tasks: int
    info_text: str
    tooltip_text: str


@dataclass
class EntitlementInfo:
    """Feature Entitlements"""
    features: List[str]
    max_team_members: int = 1
    priority_support: bool = False
    custom_branding: bool = False
    batch_processing: bool = False
    custom_templates: bool = False
    fair_use_mode: bool = False


@dataclass
class BillingStatus:
    """Vollstaendiger Billing Status"""
    has_subscription: bool
    subscription: Optional[SubscriptionInfo] = None
    task_usage: Optional[TaskUsageInfo] = None
    entitlements: Optional[EntitlementInfo] = None
    available_plans: List[BillingPlan] = field(default_factory=list)


@dataclass
class CheckoutSession:
    """Stripe Checkout Session"""
    checkout_url: str
    session_id: str


@dataclass
class UsageCheckResult:
    """Ergebnis einer Usage-Pruefung"""
    allowed: bool
    current_usage: int
    limit: int
    remaining: int
    message: Optional[str] = None


@dataclass
class EntitlementCheckResult:
    """Ergebnis einer Entitlement-Pruefung"""
    has_entitlement: bool
    plan_id: Optional[PlanID] = None
    message: Optional[str] = None


class BillingClient:
    """Client fuer die Kommunikation mit dem Billing Service"""

    def __init__(self, base_url: str = BILLING_SERVICE_URL):
        self.base_url = base_url.rstrip("/")
        self.api_url = f"{self.base_url}/api/v1/billing"

    def _get_headers(self, jwt_token: str) -> Dict[str, str]:
        """Erstellt die Header mit JWT Token"""
        return {
            "Authorization": f"Bearer {jwt_token}",
            "Content-Type": "application/json"
        }

    def _parse_plan_features(self, data: Dict[str, Any]) -> PlanFeatures:
        """Parst PlanFeatures aus JSON"""
        return PlanFeatures(
            monthly_task_allowance=data.get("monthly_task_allowance", 0),
            max_task_balance=data.get("max_task_balance", 0),
            feature_flags=data.get("feature_flags", []),
            max_team_members=data.get("max_team_members", 1),
            priority_support=data.get("priority_support", False),
            custom_branding=data.get("custom_branding", False),
            batch_processing=data.get("batch_processing", False),
            custom_templates=data.get("custom_templates", False),
            fair_use_mode=data.get("fair_use_mode", False),
        )

    def _parse_plan(self, data: Dict[str, Any]) -> BillingPlan:
        """Parst BillingPlan aus JSON"""
        return BillingPlan(
            id=PlanID(data["id"]),
            name=data["name"],
            description=data.get("description", ""),
            price_cents=data.get("price_cents", 0),
            currency=data.get("currency", "eur"),
            interval=data.get("interval", "month"),
            features=self._parse_plan_features(data.get("features", {})),
            is_active=data.get("is_active", True),
            sort_order=data.get("sort_order", 0),
        )

    # =============================================
    # User Endpoints (Auth required)
    # =============================================

    async def get_billing_status(self, jwt_token: str) -> BillingStatus:
        """
        Holt den aktuellen Billing Status inkl. Subscription, Usage und Entitlements.
        GET /api/v1/billing/status
        """
        async with httpx.AsyncClient() as client:
            try:
                response = await client.get(
                    f"{self.api_url}/status",
                    headers=self._get_headers(jwt_token),
                    timeout=10.0
                )

                if response.status_code == 200:
                    data = response.json()

                    subscription = None
                    if data.get("subscription"):
                        sub = data["subscription"]
                        subscription = SubscriptionInfo(
                            plan_id=PlanID(sub["plan_id"]),
                            plan_name=sub.get("plan_name", ""),
                            status=SubscriptionStatus(sub["status"]),
                            is_trialing=sub.get("is_trialing", False),
                            cancel_at_period_end=sub.get("cancel_at_period_end", False),
                            price_cents=sub.get("price_cents", 0),
                            currency=sub.get("currency", "eur"),
                            trial_days_left=sub.get("trial_days_left", 0),
                            current_period_end=sub.get("current_period_end"),
                        )

                    task_usage = None
                    if data.get("task_usage"):
                        tu = data["task_usage"]
                        task_usage = TaskUsageInfo(
                            tasks_available=tu.get("tasks_available", 0),
                            max_tasks=tu.get("max_tasks", 0),
                            info_text=tu.get("info_text", ""),
                            tooltip_text=tu.get("tooltip_text", ""),
                        )

                    entitlements = None
                    if data.get("entitlements"):
                        ent = data["entitlements"]
                        entitlements = EntitlementInfo(
                            features=ent.get("features", []),
                            max_team_members=ent.get("max_team_members", 1),
                            priority_support=ent.get("priority_support", False),
                            custom_branding=ent.get("custom_branding", False),
                            batch_processing=ent.get("batch_processing", False),
                            custom_templates=ent.get("custom_templates", False),
                            fair_use_mode=ent.get("fair_use_mode", False),
                        )

                    plans = []
                    for p in data.get("available_plans", []):
                        plans.append(self._parse_plan(p))

                    return BillingStatus(
                        has_subscription=data.get("has_subscription", False),
                        subscription=subscription,
                        task_usage=task_usage,
                        entitlements=entitlements,
                        available_plans=plans,
                    )
                else:
                    return BillingStatus(has_subscription=False)

            except httpx.RequestError:
                # Bei Verbindungsproblemen: Leeren Status zurueckgeben
                return BillingStatus(has_subscription=False)

    async def get_plans(self, jwt_token: str) -> List[BillingPlan]:
        """
        Holt alle verfuegbaren Billing Plans.
        GET /api/v1/billing/plans
        """
        async with httpx.AsyncClient() as client:
            try:
                response = await client.get(
                    f"{self.api_url}/plans",
                    headers=self._get_headers(jwt_token),
                    timeout=10.0
                )

                if response.status_code == 200:
                    data = response.json()
                    return [self._parse_plan(p) for p in data.get("plans", [])]
                return []

            except httpx.RequestError:
                return []

    async def start_trial(
        self,
        jwt_token: str,
        plan_id: PlanID
    ) -> Optional[CheckoutSession]:
        """
        Startet einen Trial fuer den angegebenen Plan.
        Gibt eine Stripe Checkout URL zurueck.
        POST /api/v1/billing/trial/start
        """
        async with httpx.AsyncClient() as client:
            try:
                response = await client.post(
                    f"{self.api_url}/trial/start",
                    headers=self._get_headers(jwt_token),
                    json={"plan_id": plan_id.value},
                    timeout=10.0
                )

                if response.status_code == 200:
                    data = response.json()
                    return CheckoutSession(
                        checkout_url=data["checkout_url"],
                        session_id=data["session_id"],
                    )
                return None

            except httpx.RequestError:
                return None

    async def change_plan(
        self,
        jwt_token: str,
        new_plan_id: PlanID
    ) -> bool:
        """
        Wechselt zu einem anderen Plan.
        POST /api/v1/billing/change-plan
        """
        async with httpx.AsyncClient() as client:
            try:
                response = await client.post(
                    f"{self.api_url}/change-plan",
                    headers=self._get_headers(jwt_token),
                    json={"new_plan_id": new_plan_id.value},
                    timeout=10.0
                )
                return response.status_code == 200

            except httpx.RequestError:
                return False

    async def cancel_subscription(self, jwt_token: str) -> bool:
        """
        Kuendigt das Abo zum Periodenende.
        POST /api/v1/billing/cancel
        """
        async with httpx.AsyncClient() as client:
            try:
                response = await client.post(
                    f"{self.api_url}/cancel",
                    headers=self._get_headers(jwt_token),
                    timeout=10.0
                )
                return response.status_code == 200

            except httpx.RequestError:
                return False

    async def get_customer_portal_url(self, jwt_token: str) -> Optional[str]:
        """
        Holt die Stripe Customer Portal URL.
        GET /api/v1/billing/portal
        """
        async with httpx.AsyncClient() as client:
            try:
                response = await client.get(
                    f"{self.api_url}/portal",
                    headers=self._get_headers(jwt_token),
                    timeout=10.0
                )

                if response.status_code == 200:
                    return response.json().get("portal_url")
                return None

            except httpx.RequestError:
                return None

    # =============================================
    # Internal Endpoints (Service-to-Service)
    # =============================================

    async def get_entitlements(self, user_id: str) -> Optional[EntitlementInfo]:
        """
        Holt die Entitlements fuer einen User (intern).
        GET /api/v1/billing/entitlements/:userId
        """
        async with httpx.AsyncClient() as client:
            try:
                response = await client.get(
                    f"{self.api_url}/entitlements/{user_id}",
                    timeout=10.0
                )

                if response.status_code == 200:
                    data = response.json()
                    return EntitlementInfo(
                        features=data.get("features", {}).get("feature_flags", []),
                        max_team_members=data.get("features", {}).get("max_team_members", 1),
                        priority_support=data.get("features", {}).get("priority_support", False),
                        custom_branding=data.get("features", {}).get("custom_branding", False),
                        batch_processing=data.get("features", {}).get("batch_processing", False),
                        custom_templates=data.get("features", {}).get("custom_templates", False),
                        fair_use_mode=data.get("features", {}).get("fair_use_mode", False),
                    )
                return None

            except httpx.RequestError:
                return None

    async def check_entitlement(
        self,
        user_id: str,
        feature: str
    ) -> EntitlementCheckResult:
        """
        Prueft ob ein User ein bestimmtes Feature hat (intern).
        GET /api/v1/billing/entitlements/check/:userId/:feature
        """
        async with httpx.AsyncClient() as client:
            try:
                response = await client.get(
                    f"{self.api_url}/entitlements/check/{user_id}/{feature}",
                    timeout=10.0
                )

                if response.status_code == 200:
                    data = response.json()
                    return EntitlementCheckResult(
                        has_entitlement=data.get("has_entitlement", False),
                        plan_id=PlanID(data["plan_id"]) if data.get("plan_id") else None,
                        message=data.get("message"),
                    )
                return EntitlementCheckResult(has_entitlement=False)

            except httpx.RequestError:
                # Bei Fehler: Zugriff erlauben (fail open)
                return EntitlementCheckResult(has_entitlement=True)

    async def track_usage(
        self,
        user_id: str,
        usage_type: str,
        quantity: int = 1
    ) -> bool:
        """
        Trackt Usage fuer einen User (intern).
        POST /api/v1/billing/usage/track
        """
        async with httpx.AsyncClient() as client:
            try:
                response = await client.post(
                    f"{self.api_url}/usage/track",
                    json={
                        "user_id": user_id,
                        "usage_type": usage_type,
                        "quantity": quantity,
                    },
                    timeout=10.0
                )
                return response.status_code == 200

            except httpx.RequestError:
                return False

    async def check_usage(
        self,
        user_id: str,
        usage_type: str
    ) -> UsageCheckResult:
        """
        Prueft ob Usage erlaubt ist (intern).
        GET /api/v1/billing/usage/check/:userId/:type
        """
        async with httpx.AsyncClient() as client:
            try:
                response = await client.get(
                    f"{self.api_url}/usage/check/{user_id}/{usage_type}",
                    timeout=10.0
                )

                if response.status_code == 200:
                    data = response.json()
                    return UsageCheckResult(
                        allowed=data.get("allowed", False),
                        current_usage=data.get("current_usage", 0),
                        limit=data.get("limit", 0),
                        remaining=data.get("remaining", 0),
                        message=data.get("message"),
                    )
                return UsageCheckResult(
                    allowed=False,
                    current_usage=0,
                    limit=0,
                    remaining=0,
                    message="Failed to check usage",
                )

            except httpx.RequestError:
                # Bei Fehler: Zugriff erlauben (fail open)
                return UsageCheckResult(
                    allowed=True,
                    current_usage=0,
                    limit=9999,
                    remaining=9999,
                )

    # =============================================
    # Convenience Methods
    # =============================================

    async def has_active_subscription(self, jwt_token: str) -> bool:
        """Prueft ob der User ein aktives Abo hat"""
        status = await self.get_billing_status(jwt_token)
        if not status.has_subscription or not status.subscription:
            return False
        return status.subscription.status in [
            SubscriptionStatus.ACTIVE,
            SubscriptionStatus.TRIALING
        ]

    async def has_feature(self, jwt_token: str, feature: str) -> bool:
        """Prueft ob der User ein bestimmtes Feature hat"""
        status = await self.get_billing_status(jwt_token)
        if not status.entitlements:
            return False
        return feature in status.entitlements.features

    async def can_use_batch_processing(self, jwt_token: str) -> bool:
        """Prueft ob der User Batch-Verarbeitung nutzen kann"""
        status = await self.get_billing_status(jwt_token)
        if not status.entitlements:
            return False
        return status.entitlements.batch_processing

    async def can_use_custom_templates(self, jwt_token: str) -> bool:
        """Prueft ob der User Custom Templates nutzen kann"""
        status = await self.get_billing_status(jwt_token)
        if not status.entitlements:
            return False
        return status.entitlements.custom_templates

    async def get_tasks_available(self, jwt_token: str) -> int:
        """Gibt die Anzahl verfuegbarer Tasks zurueck"""
        status = await self.get_billing_status(jwt_token)
        if not status.task_usage:
            return 0
        return status.task_usage.tasks_available

    async def health_check(self) -> bool:
        """Prueft ob der Billing Service erreichbar ist"""
        async with httpx.AsyncClient() as client:
            try:
                response = await client.get(
                    f"{self.base_url}/health",
                    timeout=5.0
                )
                return response.status_code == 200

            except httpx.RequestError:
                return False


# Singleton-Instanz fuer einfachen Zugriff
billing_client = BillingClient()
