import os
import httpx
import json
from dotenv import load_dotenv
from fastapi.templating import Jinja2Templates
from fastapi import APIRouter, HTTPException, status, Depends, Request, Cookie, Depends
from fastapi.responses import RedirectResponse
from fastapi.security import OAuth2PasswordBearer
from fastapi_sso.sso.google import GoogleSSO
from jose import jwt, JWTError
from fastapi.responses import RedirectResponse, JSONResponse 
from schemas.user import Token, UserBase, User
from utils.jwt import create_access_token, verify_token # Our custom JWT utils
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from datetime import datetime
from sqlalchemy import Column, Integer, String, DateTime
from typing import Generator
from sqlalchemy.orm import Session
from urllib.parse import quote_plus
load_dotenv()
DJANGO_API_BASE = os.getenv("DJANGO_API_BASE")
DATABASE_URL = os.getenv('DATABASE_URL')
Base = declarative_base()
engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

templates = Jinja2Templates(directory="templates")
router = APIRouter(prefix="/auth", tags=["auth"])

# --- Google SSO Configuration ---
GOOGLE_CLIENT_ID = os.getenv("GOOGLE_CLIENT_ID")
GOOGLE_CLIENT_SECRET = os.getenv("GOOGLE_CLIENT_SECRET")
GOOGLE_REDIRECT_URI = os.getenv("GOOGLE_REDIRECT_URI")
JWT_SECRET_KEY = os.getenv("JWT_SECRET_KEY")
JWT_ALGORITHM = os.getenv("ALGORITHM")

if not GOOGLE_CLIENT_ID or not GOOGLE_CLIENT_SECRET or not GOOGLE_REDIRECT_URI or not JWT_SECRET_KEY:
    raise ValueError("Missing required environment variables for Google SSO or JWT.")

google_sso = GoogleSSO(
    client_id=GOOGLE_CLIENT_ID,
    client_secret=GOOGLE_CLIENT_SECRET,
    redirect_uri=GOOGLE_REDIRECT_URI,
    allow_insecure_http=True # Set to False in production for HTTPS
)

# --- OAuth2 Bearer Token Security Scheme for Protected Endpoints ---
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/token") # Where clients get their token


def get_db() -> Generator:
    """Dependency to get a database session."""
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()
        
code_list = []  



@router.get("/google/login/{invite_code}")
async def google_login(invite_code: str, request: Request): 
    """
    1. Validates the invite code with Django.
    2. Stores the valid code in a secure, HTTP-only cookie.
    3. Initiates the Google SSO redirect.
    """
    
    # 1. Validate the invite code with Django API
    async with httpx.AsyncClient(follow_redirects=False) as client:
        
        dj_response = await client.get(
            f"{DJANGO_API_BASE}/user/validate-invite/{invite_code}/"
        )
                
        # Handle validation errors
        if dj_response.status_code != status.HTTP_200_OK:
            print(f"Error validating invite code: {dj_response.status_code}")
            try:
                error = dj_response.json().get('error', 'Unauthorized access')
            except:
                error = f"API validation failed with status: {dj_response.status_code}"
            
            return RedirectResponse(url=f"/unauthorized/{error}")
    
    # 2. Get the Google redirect URL
    redirect_uri = "https://lm.warals.codetorise.com/auth/google/callback"
    async with google_sso:
        google_redirect_url = await google_sso.get_login_redirect(redirect_uri=redirect_uri)
    
    # Extract the URL from the response headers (google_sso returns a RedirectResponse object)
    sso_url = google_redirect_url.headers.get('location')
    if not sso_url:
        return RedirectResponse(url="/login?error=sso_setup_fail", status_code=status.HTTP_302_FOUND)
        
    # 3. Create a FastAPI RedirectResponse to set the cookie
    response = RedirectResponse(url=sso_url)
    
    # 4. Store the invite_code in a temporary, http-only cookie
    # This cookie will be sent ONLY to the callback endpoint, not any random page.
    response.set_cookie(
        key="invite_code",
        value=invite_code,
        httponly=True,
        max_age=300, # Expires in 5 minutes (more than enough time for SSO)
        path="/auth/google/callback", # CRUCIAL: Only sent to the callback URL
        # secure=True, # Recommended in production with HTTPS
        samesite="Lax"
    )
    
    
    return response

@router.get("/google/callback")
async def google_callback(
    request: Request,  
    db: Session = Depends(get_db), # Assuming this is your DB dependency
    # 🚨 CRITICAL CHANGE: Read the invite_code from the cookie
    invite_code: str = Cookie(None, alias="invite_code") 
):
    """
    1. Retrieves the invite_code from the cookie.
    2. Processes Google SSO authentication.
    3. Calls Django to create/login the user, passing the invite_code.
    """
    
    # 1. Check for the invite code
    if not invite_code:
        # This will catch users trying to access the callback without starting the login flow.
        return RedirectResponse(url="/login?error=no_invite_code_found", status_code=status.HTTP_302_FOUND)

    
    
    try:
        # 2. Verify and process Google SSO
        async with google_sso:
             # The redirect_uri argument here is optional if already configured globally
             user_sso = await google_sso.verify_and_process(request) 
        
        if not user_sso or not user_sso.email:
            return RedirectResponse(url="/login?error=auth_failed", status_code=status.HTTP_302_FOUND)
            
        # 3. Create your JWT access token
        access_token = create_access_token(data={
            "email": user_sso.email,
            "name": user_sso.display_name,
        })
        
        # 4. Call Django API to create or fetch the user
        async with httpx.AsyncClient(follow_redirects=False) as client:
            
            
            # Pass the invite_code in the JSON payload for Django to handle
            dj_response = await client.post(
                f"{DJANGO_API_BASE}/user/users/",
                json={"email": user_sso.email, "name": user_sso.display_name, "invitation_code": invite_code},
                headers={"Authorization": f"Bearer {access_token}"}
            )
            
            
            
            # Check for Django user creation/login errors
            if dj_response.status_code != status.HTTP_200_OK:
                # The 429 error would typically happen on the validation call, 
                # but we handle any non-200 here just in case.
                error = dj_response.json().get('error', f'Django user API failed with status: {dj_response.status_code}')
                # You might need to adjust the redirect URL based on your specific needs
                return RedirectResponse(url=f"/login?error={error}", status_code=status.HTTP_302_FOUND)
        
            user_data = dj_response.json()
        
        # 5. Final successful response: Set JWT cookie and redirect
        response = RedirectResponse(url="/home", status_code=status.HTTP_302_FOUND)
        response.set_cookie(
            key="access_token",
            value=access_token,
            httponly=True,
            max_age=3600, 
            expires=3600,
            path="/",
            # secure=True, # Recommended in production with HTTPS
            samesite="Lax"
        )
        
        
        return response

    except Exception as e:
        # General catch-all for SSO or internal errors
        print(f"Error during Google callback: {e}")
        # print(traceback.format_exc()) # Uncomment for deep debugging
        return RedirectResponse(url=f"/login?error=sso_processing_error", status_code=status.HTTP_302_FOUND)




# end points for mobile application
# --- Google Login Endpoint (Mobile / JSON flow) ---
@router.get("/google/login/mobile/{invite_code}")
async def google_login_mobile(invite_code: str, request: Request): 
    """Initiates the Google SSO login process (Mobile, JSON)."""
    code_list.append(invite_code)
    #validate-invite/<str:invite_code>/
    async with httpx.AsyncClient(follow_redirects=False) as client:
        
        dj_response = await client.get(
            f"{DJANGO_API_BASE}/user/validate-invite/{code_list[-1]}/"
        )
                
        # Check if the Django API call was successful
        if dj_response.status_code != 200:
            
            
            error = dj_response.json().get('error', 'Unauthorized access')
            print(f"Error validating invite code: {error}")
            return {"success": False, "error": error}

    # Return Google redirect URL instead of actual redirect
    login_redirect = await google_sso.get_login_redirect(redirect_uri="https://lm.warals.codetorise.com/auth/google/callback/mobile")
    return {"success": True, "login_url": str(login_redirect.headers.get("location"))}

# --- Google Callback Endpoint (Mobile / JSON flow) ---
@router.get("/google/callback/mobile")
async def google_callback_mobile(request: Request, db: Session = Depends(get_db)):
    """Handles the callback from Google after successful authentication (Mobile, JSON -> REDIRECT)."""
    
    # ðŸš¨ 1. DEFINE THE MOBILE APP'S CUSTOM SCHEME
    # This MUST match the `callbackUrlScheme` in your Flutter code (e.g., 'myapp')
    MOBILE_CALLBACK_SCHEME = "myapp" 
    MOBILE_CALLBACK_PATH = "callback"
    
    
    invite_code = code_list[-1]
    
    # --- (Invite Code Validation and SSO Process remain the same) ---
    async with httpx.AsyncClient(follow_redirects=False) as client:
        dj_response = await client.get(
            f"{DJANGO_API_BASE}/user/validate-invite/{code_list[-1]}/"
        )
                
        if dj_response.status_code != 200:
            error = dj_response.json().get('error', 'Unauthorized access')
            # ðŸš¨ Handle failure by redirecting with an error parameter
            error_url = f"{MOBILE_CALLBACK_SCHEME}://{MOBILE_CALLBACK_PATH}?success=false&error={quote_plus(error)}"
            return RedirectResponse(url=error_url, status_code=302)

    try:
        async with google_sso:
            user_sso = await google_sso.verify_and_process(request, redirect_uri="https://lm.warals.codetorise.com/auth/google/callback/mobile")

        if not user_sso or not user_sso.email:
            error_url = f"{MOBILE_CALLBACK_SCHEME}://{MOBILE_CALLBACK_PATH}?success=false&error=auth_failed"
            return RedirectResponse(url=error_url, status_code=302)

        access_token = create_access_token(data={
            "email": user_sso.email,
            "name": user_sso.display_name,
        })

        async with httpx.AsyncClient(follow_redirects=False) as client:
            dj_response = await client.post(
                f"{DJANGO_API_BASE}/user/users/",
                json={"email": user_sso.email, "name": user_sso.display_name, "invitation_code": invite_code},
                headers={"Authorization": f"Bearer {access_token}"}
            )
            
            if dj_response.status_code != 200:
                error = dj_response.json().get('error')
                error_url = f"{MOBILE_CALLBACK_SCHEME}://{MOBILE_CALLBACK_PATH}?success=false&error={quote_plus(error)}"
                return RedirectResponse(url=error_url, status_code=302)
        
            user_data = dj_response.json()

        
        
        # --- 2. CONSTRUCT THE FINAL JSON PAYLOAD ---
        final_payload = {
            "success": True,
            "access_token": access_token,
            "token_type": "bearer",
            "expires_in": 3600,
            "user": user_data
        }
        
        # --- 3. URL-ENCODE THE PAYLOAD ---
        # Convert dict to JSON string, then URL-encode the string
        json_string = json.dumps(final_payload)
        encoded_json = quote_plus(json_string)
        
        # --- 4. ISSUE THE REDIRECT (The key change!) ---
        # The mobile app will catch this URL and extract the 'data' parameter.
        redirect_url = f"{MOBILE_CALLBACK_SCHEME}://{MOBILE_CALLBACK_PATH}?data={encoded_json}"
        
        # Send the HTTP 302 Redirect to the client's web view
        return RedirectResponse(url=redirect_url, status_code=302)

    except Exception as e:
        print(f"Error during Google callback: {e}")
        
        # ðŸš¨ Handle general exception failure
        error_url = f"{MOBILE_CALLBACK_SCHEME}://{MOBILE_CALLBACK_PATH}?success=false&error={quote_plus(str(e))}"
        return RedirectResponse(url=error_url, status_code=302)




# Your get_current_user dependency would then read from the cookie:
async def get_current_user(request: Request, db: Session = Depends(get_db)) -> UserBase:
    token = request.cookies.get("access_token")
    
    
    if not token:
        # raise HTTPException(
        #     status_code=status.HTTP_401_UNAUTHORIZED,
        #     detail="Not authenticated",
        #     headers={"WWW-Authenticate": "Bearer"},
        # )
        return templates.TemplateResponse("login.html", {"request": request})
    
    
    payload = verify_token(token)
    
    if not payload:
        return templates.TemplateResponse("login.html", {"request": request})
        
        # raise HTTPException(
        #     status_code=status.HTTP_401_UNAUTHORIZED,
        #     detail="Could not validate credentials",
        #     headers={"WWW-Authenticate": "Bearer"},
        # )
    # Fetch user from DB based on email
    # db_user = db.query(User).filter(User.email == payload.get('email')).first()
    # if db_user is None:
    #     # This case should ideally not happen if user is created on callback
    #     # But good for robustness, e.g., if DB was reset.
    #     #return templates.TemplateResponse("login.html", {"request": request})
    #     raise HTTPException(
    #         status_code=status.HTTP_401_UNAUTHORIZED,
    #         detail="User not found in database",
    #         headers={"WWW-Authenticate": "Bearer"},
    #     )

    # Return UserBase schema from DB user data
    # print(f"Current user: {db_user.email}, Name: {db_user.name}")
    # return UserBase(email=db_user.email, name=db_user.name)
    user_data = UserBase(email=payload.get("email"), name=payload.get("name"))
    return user_data

# --- Example Protected Endpoint ---
@router.get("/me", response_model=UserBase)
async def read_current_user(current_user: UserBase = Depends(get_current_user)):
    """Returns information about the current authenticated user."""
    return current_user


def create_db_tables():
    Base.metadata.create_all(bind=engine)

# --- Dependency for Database Session ---
def get_db() -> Generator:
    """Dependency to get a database session."""
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()