"""
Gateway middleware to enforce read-only mode for subscriptions with status:
- pending_cancellation (until cancellation_effective_date)
- inactive (after cancellation or no active subscription)

Allowed operations in read-only mode:
- GET requests (all read operations)
- POST /api/v1/users/me/delete/request (account deletion)
- POST /api/v1/subscriptions/reactivate (subscription reactivation)
- POST /api/v1/subscriptions/* (subscription management)
"""

import httpx
import logging
from fastapi import Request, HTTPException, status
from fastapi.responses import JSONResponse
from starlette.middleware.base import BaseHTTPMiddleware
from typing import Optional
import re

logger = logging.getLogger(__name__)

# Whitelist of POST/PUT/DELETE endpoints allowed in read-only mode
READ_ONLY_WHITELIST_PATTERNS = [
    r'^/api/v1/users/me/delete/request$',
    r'^/api/v1/users/me/export.*$',
    r'^/api/v1/tenants/.*/subscription/.*',  # All tenant subscription endpoints
    r'^/api/v1/registration/.*',  # Registration flow endpoints
    r'^/api/v1/auth/.*',  # Allow auth operations
    r'^/api/v1/tenants/register$',  # Allow new tenant registration (no existing tenant context)
    r'^/api/v1/tenants/.*/orchestrator/run-daily-workflow$',  # Allow workflow testing
    r'^/api/v1/tenants/.*/inventory/ml/insights/.*',  # Allow ML insights (safety stock optimization)
    r'^/api/v1/tenants/.*/production/ml/insights/.*',  # Allow ML insights (yield prediction)
    r'^/api/v1/tenants/.*/procurement/ml/insights/.*',  # Allow ML insights (supplier analysis, price forecasting)
    r'^/api/v1/tenants/.*/forecasting/ml/insights/.*',  # Allow ML insights (rules generation)
    r'^/api/v1/tenants/.*/forecasting/operations/.*',  # Allow forecasting operations
    r'^/api/v1/webhooks/.*',  # Webhook endpoints - no tenant context
]


class ReadOnlyModeMiddleware(BaseHTTPMiddleware):
    """
    Middleware to enforce read-only mode based on subscription status
    """

    def __init__(self, app, tenant_service_url: str = "http://tenant-service:8000"):
        super().__init__(app)
        self.tenant_service_url = tenant_service_url
        self.cache = {}
        self.cache_ttl = 60

    async def check_subscription_status(self, tenant_id: str, authorization: str) -> dict:
        """
        Check subscription status from tenant service
        Returns subscription data including status and read_only flag
        """
        try:
            async with httpx.AsyncClient(timeout=5.0) as client:
                response = await client.get(
                    f"{self.tenant_service_url}/api/v1/tenants/{tenant_id}/subscription/status",
                    headers={"Authorization": authorization}
                )

                if response.status_code == 200:
                    return response.json()
                elif response.status_code == 404:
                    return {"status": "inactive", "is_read_only": True}
                else:
                    logger.warning(
                        f"Failed to check subscription status: {response.status_code}",
                        extra={"tenant_id": tenant_id}
                    )
                    return {"status": "unknown", "is_read_only": False}

        except Exception as e:
            logger.error(
                f"Error checking subscription status: {e}",
                extra={"tenant_id": tenant_id}
            )
            return {"status": "unknown", "is_read_only": False}

    def is_whitelisted_endpoint(self, path: str) -> bool:
        """
        Check if endpoint is whitelisted for read-only mode
        """
        for pattern in READ_ONLY_WHITELIST_PATTERNS:
            if re.match(pattern, path):
                return True
        return False

    def is_write_operation(self, method: str) -> bool:
        """
        Determine if HTTP method is a write operation
        """
        return method.upper() in ['POST', 'PUT', 'DELETE', 'PATCH']

    async def dispatch(self, request: Request, call_next):
        """
        Process each request through read-only mode check
        """
        tenant_id = request.headers.get("X-Tenant-ID")
        authorization = request.headers.get("Authorization")
        path = request.url.path
        method = request.method

        if not tenant_id or not authorization:
            return await call_next(request)

        if method.upper() == 'GET':
            return await call_next(request)

        if self.is_whitelisted_endpoint(path):
            return await call_next(request)

        if self.is_write_operation(method):
            subscription_data = await self.check_subscription_status(tenant_id, authorization)

            if subscription_data.get("is_read_only", False):
                status_detail = subscription_data.get("status", "inactive")
                effective_date = subscription_data.get("cancellation_effective_date")

                error_message = {
                    "detail": "Account is in read-only mode",
                    "reason": f"Subscription status: {status_detail}",
                    "message": "Your subscription has been cancelled. You can view data but cannot make changes.",
                    "action_required": "Reactivate your subscription to regain full access",
                    "reactivation_url": "/app/settings/subscription"
                }

                if effective_date:
                    error_message["read_only_until"] = effective_date
                    error_message["message"] = f"Your subscription is pending cancellation. Read-only mode starts on {effective_date}."

                logger.info(
                    "read_only_mode_enforced",
                    extra={
                        "tenant_id": tenant_id,
                        "path": path,
                        "method": method,
                        "subscription_status": status_detail
                    }
                )

                return JSONResponse(
                    status_code=status.HTTP_403_FORBIDDEN,
                    content=error_message
                )

        return await call_next(request)
