from fastapi import APIRouter, Depends, HTTPException, status, Cookie, Response, Request, UploadFile, File
from fastapi.responses import JSONResponse
from sqlalchemy.orm import Session
from pathlib import Path
import aiofiles
import uuid
import os
from ..config.database import get_db
from ..services.auth_service import auth_service
from ..schemas.auth import RegisterRequest, LoginRequest, RefreshTokenRequest, ForgotPasswordRequest, ResetPasswordRequest, AuthResponse, TokenResponse, MessageResponse, MFAInitResponse, EnableMFARequest, VerifyMFARequest, MFAStatusResponse
from ..middleware.auth import get_current_user
from ..models.user import User
router = APIRouter(prefix='/auth', tags=['auth'])

def get_base_url(request: Request) -> str:
    return os.getenv('SERVER_URL') or f'http://{request.headers.get('host', 'localhost:8000')}'

def normalize_image_url(image_url: str, base_url: str) -> str:
    if not image_url:
        return image_url
    if image_url.startswith('http://') or image_url.startswith('https://'):
        return image_url
    if image_url.startswith('/'):
        return f'{base_url}{image_url}'
    return f'{base_url}/{image_url}'

@router.post('/register', status_code=status.HTTP_201_CREATED)
async def register(request: RegisterRequest, response: Response, db: Session=Depends(get_db)):
    try:
        result = await auth_service.register(db=db, name=request.name, email=request.email, password=request.password, phone=request.phone)
        response.set_cookie(key='refreshToken', value=result['refreshToken'], httponly=True, secure=False, samesite='strict', max_age=7 * 24 * 60 * 60, path='/')
        return {'status': 'success', 'message': 'Registration successful', 'data': {'token': result['token'], 'user': result['user']}}
    except ValueError as e:
        error_message = str(e)
        return JSONResponse(status_code=status.HTTP_400_BAD_REQUEST, content={'status': 'error', 'message': error_message})

@router.post('/login')
async def login(request: LoginRequest, response: Response, db: Session=Depends(get_db)):
    try:
        result = await auth_service.login(db=db, email=request.email, password=request.password, remember_me=request.rememberMe or False, mfa_token=request.mfaToken)
        if result.get('requires_mfa'):
            return {'status': 'success', 'requires_mfa': True, 'user_id': result['user_id']}
        max_age = 7 * 24 * 60 * 60 if request.rememberMe else 1 * 24 * 60 * 60
        response.set_cookie(key='refreshToken', value=result['refreshToken'], httponly=True, secure=False, samesite='strict', max_age=max_age, path='/')
        return {'status': 'success', 'data': {'token': result['token'], 'user': result['user']}}
    except ValueError as e:
        error_message = str(e)
        status_code = status.HTTP_401_UNAUTHORIZED if 'Invalid email or password' in error_message or 'Invalid MFA token' in error_message else status.HTTP_400_BAD_REQUEST
        return JSONResponse(status_code=status_code, content={'status': 'error', 'message': error_message})

@router.post('/refresh-token', response_model=TokenResponse)
async def refresh_token(refreshToken: str=Cookie(None), db: Session=Depends(get_db)):
    if not refreshToken:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail='Refresh token not found')
    try:
        result = await auth_service.refresh_access_token(db, refreshToken)
        return result
    except ValueError as e:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=str(e))

@router.post('/logout', response_model=MessageResponse)
async def logout(response: Response, refreshToken: str=Cookie(None), db: Session=Depends(get_db)):
    if refreshToken:
        await auth_service.logout(db, refreshToken)
    response.delete_cookie(key='refreshToken', path='/')
    return {'status': 'success', 'message': 'Logout successful'}

@router.get('/profile')
async def get_profile(current_user: User=Depends(get_current_user), db: Session=Depends(get_db)):
    try:
        user = await auth_service.get_profile(db, current_user.id)
        return {'status': 'success', 'data': {'user': user}}
    except ValueError as e:
        if 'User not found' in str(e):
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))

@router.put('/profile')
async def update_profile(profile_data: dict, current_user: User=Depends(get_current_user), db: Session=Depends(get_db)):
    try:
        user = await auth_service.update_profile(db=db, user_id=current_user.id, full_name=profile_data.get('full_name'), email=profile_data.get('email'), phone_number=profile_data.get('phone_number'), password=profile_data.get('password'), current_password=profile_data.get('currentPassword'), currency=profile_data.get('currency'))
        return {'status': 'success', 'message': 'Profile updated successfully', 'data': {'user': user}}
    except ValueError as e:
        error_message = str(e)
        status_code = status.HTTP_400_BAD_REQUEST
        if 'not found' in error_message.lower():
            status_code = status.HTTP_404_NOT_FOUND
        raise HTTPException(status_code=status_code, detail=error_message)
    except Exception as e:
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f'An error occurred: {str(e)}')

@router.post('/forgot-password', response_model=MessageResponse)
async def forgot_password(request: ForgotPasswordRequest, db: Session=Depends(get_db)):
    result = await auth_service.forgot_password(db, request.email)
    return {'status': 'success', 'message': result['message']}

@router.post('/reset-password', response_model=MessageResponse)
async def reset_password(request: ResetPasswordRequest, db: Session=Depends(get_db)):
    try:
        result = await auth_service.reset_password(db=db, token=request.token, password=request.password)
        return {'status': 'success', 'message': result['message']}
    except ValueError as e:
        status_code = status.HTTP_400_BAD_REQUEST
        if 'User not found' in str(e):
            status_code = status.HTTP_404_NOT_FOUND
        raise HTTPException(status_code=status_code, detail=str(e))
from ..services.mfa_service import mfa_service
from ..config.settings import settings

@router.get('/mfa/init')
async def init_mfa(current_user: User=Depends(get_current_user), db: Session=Depends(get_db)):
    try:
        if current_user.mfa_enabled:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail='MFA is already enabled')
        secret = mfa_service.generate_secret()
        app_name = getattr(settings, 'APP_NAME', 'Hotel Booking')
        qr_code = mfa_service.generate_qr_code(secret, current_user.email, app_name)
        return {'status': 'success', 'data': {'secret': secret, 'qr_code': qr_code}}
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f'Error initializing MFA: {str(e)}')

@router.post('/mfa/enable')
async def enable_mfa(request: EnableMFARequest, current_user: User=Depends(get_current_user), db: Session=Depends(get_db)):
    try:
        success, backup_codes = mfa_service.enable_mfa(db=db, user_id=current_user.id, secret=request.secret, verification_token=request.verification_token)
        return {'status': 'success', 'message': 'MFA enabled successfully', 'data': {'backup_codes': backup_codes}}
    except ValueError as e:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
    except Exception as e:
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f'Error enabling MFA: {str(e)}')

@router.post('/mfa/disable')
async def disable_mfa(current_user: User=Depends(get_current_user), db: Session=Depends(get_db)):
    try:
        mfa_service.disable_mfa(db=db, user_id=current_user.id)
        return {'status': 'success', 'message': 'MFA disabled successfully'}
    except ValueError as e:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
    except Exception as e:
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f'Error disabling MFA: {str(e)}')

@router.get('/mfa/status', response_model=MFAStatusResponse)
async def get_mfa_status(current_user: User=Depends(get_current_user), db: Session=Depends(get_db)):
    try:
        status_data = mfa_service.get_mfa_status(db=db, user_id=current_user.id)
        return status_data
    except ValueError as e:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
    except Exception as e:
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f'Error getting MFA status: {str(e)}')

@router.post('/mfa/regenerate-backup-codes')
async def regenerate_backup_codes(current_user: User=Depends(get_current_user), db: Session=Depends(get_db)):
    try:
        backup_codes = mfa_service.regenerate_backup_codes(db=db, user_id=current_user.id)
        return {'status': 'success', 'message': 'Backup codes regenerated successfully', 'data': {'backup_codes': backup_codes}}
    except ValueError as e:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
    except Exception as e:
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f'Error regenerating backup codes: {str(e)}')

@router.post('/avatar/upload')
async def upload_avatar(request: Request, image: UploadFile=File(...), current_user: User=Depends(get_current_user), db: Session=Depends(get_db)):
    try:
        if not image.content_type or not image.content_type.startswith('image/'):
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail='File must be an image')
        content = await image.read()
        if len(content) > 2 * 1024 * 1024:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail='Avatar file size must be less than 2MB')
        upload_dir = Path(__file__).parent.parent.parent / 'uploads' / 'avatars'
        upload_dir.mkdir(parents=True, exist_ok=True)
        if current_user.avatar:
            old_avatar_path = Path(__file__).parent.parent.parent / current_user.avatar.lstrip('/')
            if old_avatar_path.exists() and old_avatar_path.is_file():
                try:
                    old_avatar_path.unlink()
                except Exception:
                    pass
        ext = Path(image.filename).suffix or '.png'
        filename = f'avatar-{current_user.id}-{uuid.uuid4()}{ext}'
        file_path = upload_dir / filename
        async with aiofiles.open(file_path, 'wb') as f:
            await f.write(content)
        image_url = f'/uploads/avatars/{filename}'
        current_user.avatar = image_url
        db.commit()
        db.refresh(current_user)
        base_url = get_base_url(request)
        full_url = normalize_image_url(image_url, base_url)
        return {'success': True, 'status': 'success', 'message': 'Avatar uploaded successfully', 'data': {'avatar_url': image_url, 'full_url': full_url, 'user': {'id': current_user.id, 'name': current_user.full_name, 'email': current_user.email, 'phone': current_user.phone, 'avatar': image_url, 'role': current_user.role.name if current_user.role else 'customer'}}}
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f'Error uploading avatar: {str(e)}')