from fastapi import Depends, HTTPException, status, Request, APIRouter
from pydantic import BaseModel, EmailStr
from app.db import database
from app.db.database_static import get_database
from datetime import datetime, timedelta
from passlib.context import CryptContext
import re
import jwt as pyjwt
from fastapi.security import OAuth2PasswordBearer
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.responses import Response
import logging
import os
from dotenv import load_dotenv
from .routes import PROTECTED_ROUTES, UNPROTECTED_ROUTES, OPEN_CORS_ROUTES

load_dotenv()  # This line will load the .env file

# Setup
router = APIRouter()
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
SECRET_KEY = os.environ.get('JWT_SECRET_KEY')
ALGORITHM = "HS256"
TOKEN_EXPIRE_HOURS = 24
COLLECTION_NAME = "users"
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/login")
logger = logging.getLogger(__name__)

# Models
class UserLogin(BaseModel):
    email: EmailStr
    password: str

class Token(BaseModel):
    access_token: str
    token_type: str

class TokenData(BaseModel):
    email: str

# Token generation
def create_access_token(data: dict, expires_delta: timedelta = None):
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(hours=TOKEN_EXPIRE_HOURS)
    to_encode.update({"exp": expire})
    encoded_jwt = pyjwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

# JWT verification
def verify_token(token: str, db: database.MongoDB) -> dict:
    
    try:
        payload = pyjwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM], options={"verify_exp": True})
        email: str = payload.get("sub")
        if email is None:
            raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials")
    except pyjwt.InvalidSignatureError:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials or token has expired")

    user = db[COLLECTION_NAME].find_one({"email": email})
    user["id"] = str(user["_id"])
    if user is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found")
    return user

# Dependency to get the current user
def get_current_userdetails(request: Request, db: database.MongoDB = Depends(database.get_mongo_db)): 
    user = verify_token(request.state.token, db)
    request.state.current_user = user
    return request.state.current_user

def get_current_userdetails_old(request: Request):
    return request.state.current_user

def get_current_user(token: str = Depends(oauth2_scheme)):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        payload = pyjwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        email: str = payload.get("sub")
        if email is None:
            raise credentials_exception
    except :
        raise credentials_exception
    return email


# Dependency to check if user is within the account
def get_current_user_within_account(account_id: int, current_user: dict = Depends(get_current_user)):
    if current_user["account_id"] != account_id:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="No access to this account data")
    return current_user

### Managing Permissions ####

#finding permissions for specific role
def has_permission(required_role: int = 1, current_user: dict = Depends(get_current_user)):
    # Assuming you have a 'role' field in the user document
    user_role = current_user.get("role", 0)  # Default role if not present in user document
    if user_role < required_role:
        raise HTTPException(status_code=403, detail="Access forbidden. Insufficient role.")
    return current_user

# Define the dependency with required_role set to 1 (super admin)
def has_superadmin_permission(current_user: dict = Depends(get_current_user)):
    required_role = 1  # Super admin role ID
    user_role = current_user.get("role", 0)  # Default role if not present in user document
    if user_role != required_role:
        raise HTTPException(status_code=403, detail="Access forbidden. Insufficient role.")
    return current_user


# class AuthMiddleware(BaseHTTPMiddleware):
#     async def dispatch(self, request: Request, call_next):

#         print("AUTH TEST ::: ", request.cookies)
#         #print("AUTH")

#         if any(self.path_matches_route(request.url.path, route) for route in OPEN_CORS_ROUTES):
#             if request.method == "OPTIONS":
#                 #print("hello options")
#                 headers = {
#                     "Access-Control-Allow-Origin": request.headers.get("Origin"),
#                     "Access-Control-Allow-Credentials": "true",
#                     "Access-Control-Allow-Methods": "POST, GET, DELETE, PUT, OPTIONS",
#                     "Access-Control-Allow-Headers": "Authorization, Content-Type",
#                 }
#                 return Response(headers=headers)

#             response = await call_next(request)
#             response.headers["Access-Control-Allow-Origin"] = request.headers.get("Origin")
#             response.headers["Access-Control-Allow-Credentials"] = "true"
#             # Set other CORS headers as needed
#             # For example, handling preflight requests:
#             # Set other CORS headers as needed
#         else:
#             if any(self.path_matches_route(request.url.path, route) for route in UNPROTECTED_ROUTES):
#                 response = await call_next(request)
#                 return response

#             if any(self.path_matches_route(request.url.path, route) for route in PROTECTED_ROUTES):
#                 #token = request.headers.get("Authorization")

#                 token = request.cookies.get("Authorization")

#                 if not token:
#                     logger.error("Token not present in request")
#                     raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=" Token is not there")

#                 if token and token.startswith("Bearer "):
#                     token = token[7:]
#                 else:
#                     raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Access is denied")

#                 request.state.token = token

#             response = await call_next(request)

#         return response

#     @staticmethod
#     def path_matches_route(path: str, route_pattern: str) -> bool:
#         # Replace wildcard symbol with actual wildcard regex
#         pattern = route_pattern.replace('*', '.*') + '$'
#         return bool(re.match(pattern, path))

class AuthMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        print("AUTH TEST ::: ", request.cookies)

        # ✅ Bypass auth for Stripe webhook
        if request.url.path.startswith("/v1/subscriptions/stripe-webhook"):
            return await call_next(request)

        # ✅ Handle open CORS routes
        if any(self.path_matches_route(request.url.path, route) for route in OPEN_CORS_ROUTES):
            if request.method == "OPTIONS":
                headers = {
                    "Access-Control-Allow-Origin": request.headers.get("Origin"),
                    "Access-Control-Allow-Credentials": "true",
                    "Access-Control-Allow-Methods": "POST, GET, DELETE, PUT, OPTIONS",
                    "Access-Control-Allow-Headers": "Authorization, Content-Type",
                }
                return Response(headers=headers)

            response = await call_next(request)
            response.headers["Access-Control-Allow-Origin"] = request.headers.get("Origin")
            response.headers["Access-Control-Allow-Credentials"] = "true"
            return response

        # ✅ Handle unprotected routes
        if any(self.path_matches_route(request.url.path, route) for route in UNPROTECTED_ROUTES):
            return await call_next(request)

        # ✅ Protected routes need token
        if any(self.path_matches_route(request.url.path, route) for route in PROTECTED_ROUTES):
            token = request.cookies.get("Authorization")

            if not token:
                logger.error("Token not present in request")
                raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=" Token is not there")

            if token.startswith("Bearer "):
                token = token[7:]
            else:
                raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Access is denied")

            request.state.token = token

        # Continue request
        response = await call_next(request)
        return response

    @staticmethod
    def path_matches_route(path: str, route_pattern: str) -> bool:
        pattern = route_pattern.replace('*', '.*') + '$'
        return bool(re.match(pattern, path))


class AuthMiddleware_new(BaseHTTPMiddleware):
    async def dispatch_new(self, request: Request, call_next):
        # Check if the route is in UNPROTECTED_ROUTES
        if any(self.path_matches_route(request.url.path, route) for route in UNPROTECTED_ROUTES):
            response = await call_next(request)
            return response

        # Check if the route is in PROTECTED_ROUTES
        if any(self.path_matches_route(request.url.path, route) for route in PROTECTED_ROUTES):
            token = request.headers.get("Authorization")
            if not token:
                logger.error("Token not present in request")
                raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Token is not there")

            if token and token.startswith("Bearer "):
                token = token[7:]
            else:
                raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Access is denied")

            try:
                user = verify_token(token, database.get_mongo_db())
                request.state.current_user = user
            except HTTPException:
                raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Access is denied")

        response = await call_next(request)
        return response

