from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.responses import RedirectResponse
import httpx
import urllib.parse
import secrets
from datetime import timedelta
from sqlalchemy.ext.asyncio import AsyncSession

from app.api.deps import get_current_user
from app.db.session import get_db
from app.schemas.auth import (
    ForgotPasswordRequest,
    ForgotPasswordResponse,
    LoginRequest,
    LoginResponse,
    LogoutRequest,
    MessageResponse,
    RefreshRequest,
    RefreshResponse,
    ResetPasswordRequest,
    ResetPasswordResponse,
    SignupRequest,
    SignupResponse,
    TokenPair,
)
from app.schemas.user import UserOut
from app.services import auth_service
from app.core.config import settings
from app.core.security import create_token, hash_password, hash_refresh_token, utcnow
from app.models.user import User
from app.models.refresh_token import RefreshToken
from sqlalchemy import select
from dotenv import load_dotenv
load_dotenv()  # Load environment variables from .env file
router = APIRouter()


@router.post("/signup", response_model=UserOut)
async def signup(payload: SignupRequest, db: AsyncSession = Depends(get_db)):
    user = await auth_service.signup(
        db,
        email=payload.email,
        password=payload.password,
        confirm_password=payload.confirm_password,
    )
    return UserOut.model_validate(user)

@router.post("/login", response_model=LoginResponse)
async def login(payload: LoginRequest, db: AsyncSession = Depends(get_db)) -> LoginResponse:
    access_token, refresh_token = await auth_service.login(db, email=payload.email, password=payload.password)
    return LoginResponse(tokens=TokenPair(access_token=access_token, refresh_token=refresh_token))



@router.get("/callback")
async def google_callback(code: str | None = None, db: AsyncSession = Depends(get_db)) -> RedirectResponse:
    if not code:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Missing code")

    if not settings.GOOGLE_CLIENT_ID or not settings.GOOGLE_CLIENT_SECRET or not settings.GOOGLE_REDIRECT_URI:
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Google OAuth not configured")

    if not settings.OAUTH_TOKEN_URI or not settings.OAUTH_USER_INFO_URI:
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="OAuth provider URIs not configured")

    token_url = settings.OAUTH_TOKEN_URI
    userinfo_url = settings.OAUTH_USER_INFO_URI

    async with httpx.AsyncClient() as client:
        resp = await client.post(
            token_url,
            data={
                "code": code,
                "client_id": settings.GOOGLE_CLIENT_ID,
                "client_secret": settings.GOOGLE_CLIENT_SECRET,
                "redirect_uri": settings.GOOGLE_REDIRECT_URI,
                "grant_type": "authorization_code",
            },
            headers={"Accept": "application/json"},
            timeout=10.0,
        )

        if resp.status_code != 200:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Failed to exchange code")

        token_data = resp.json()
        access_token = token_data.get("access_token")
        if not access_token:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="No access token from provider")

        ui_resp = await client.get(userinfo_url, params={"access_token": access_token}, timeout=10.0)
        if ui_resp.status_code != 200:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Failed to fetch user info")
        user_info = ui_resp.json()

    email = user_info.get("email")
    if not email:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Provider did not return email")

    # find or create user
    result = await db.execute(select(User).where(User.email == email))
    user = result.scalar_one_or_none()
    if not user:
        # create a random password hash for OAuth users
        random_pw = secrets.token_urlsafe(32)
        user = User(email=email, password_hash=hash_password(random_pw))
        db.add(user)
        await db.commit()
        await db.refresh(user)

    # create application tokens
    access_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
    refresh_expires = timedelta(days=settings.REFRESH_TOKEN_EXPIRE_DAYS)

    access_jwt = create_token(sub=str(user.id), email=user.email, token_type="access", expires_delta=access_expires)
    refresh_jwt = create_token(sub=str(user.id), email=user.email, token_type="refresh", expires_delta=refresh_expires)

    rt = RefreshToken(
        user_id=user.id,
        token_hash=hash_refresh_token(refresh_jwt),
        expires_at=utcnow() + refresh_expires,
        revoked=False,
    )
    db.add(rt)
    await db.commit()

    frontend = (settings.FRONTEND_URL or "").rstrip("/")
    # pass tokens as URL-encoded query params (frontend can pick them up)
    qs = urllib.parse.urlencode({"access_token": access_jwt, "refresh_token": refresh_jwt})
    redirect_url = f"{frontend}/dashboard?{qs}"

    return RedirectResponse(url=redirect_url)


@router.post("/refresh", response_model=RefreshResponse)
async def refresh(payload: RefreshRequest, db: AsyncSession = Depends(get_db)) -> RefreshResponse:
    access_token, refresh_token = await auth_service.refresh(db, refresh_token=payload.refresh_token)
    return RefreshResponse(tokens=TokenPair(access_token=access_token, refresh_token=refresh_token))


@router.post("/logout", response_model=MessageResponse)
async def logout(payload: LogoutRequest, db: AsyncSession = Depends(get_db)) -> MessageResponse:
    await auth_service.logout(db, refresh_token=payload.refresh_token)
    return MessageResponse(message="Logged out")


@router.get("/me", response_model=UserOut)
async def me(current_user=Depends(get_current_user)) -> UserOut:
    return UserOut.model_validate(current_user)


@router.post("/forgot-password", response_model=ForgotPasswordResponse)
async def forgot_password(payload: ForgotPasswordRequest, db: AsyncSession = Depends(get_db)) -> ForgotPasswordResponse:
    found, _token = await auth_service.forgot_password(db, email=payload.email)
   
    if not found:
        raise HTTPException(
            status_code=400,
            detail="We couldn’t find an account associated with this email address. Please verify the email and try again.",
        )
 
    return ForgotPasswordResponse(message="A link has been sent to your email.")


@router.post("/reset-password/{token}", response_model=ResetPasswordResponse)
async def reset_password(token: str, payload: ResetPasswordRequest, db: AsyncSession = Depends(get_db)) -> ResetPasswordResponse:
    await auth_service.reset_password(
        db,
        token=token,
        new_password=payload.new_password,
        confirm_password=payload.confirm_password,
    )
    return ResetPasswordResponse(message="Your password has been successfully updated.")
