from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.future import select
from fastapi.security import OAuth2PasswordRequestForm

from database import get_db
from models import User, AuditLog, SystemSetting
from schemas import UserCreate, UserResponse, Token, MFAVerify
from auth import get_password_hash, verify_password, create_access_token, get_current_active_user
from mfa_utils import generate_mfa_code, send_mfa_email
from pydantic import BaseModel
import json
from datetime import datetime, timedelta
import jwt

class PasswordChange(BaseModel):
    current_password: str
    new_password: str

router = APIRouter(prefix="/api/v1/auth", tags=["auth"])

@router.get("/me", response_model=UserResponse)
async def get_current_user_profile(current_user: User = Depends(get_current_active_user)):
    return current_user

@router.post("/register", response_model=UserResponse)
async def register(user: UserCreate, db: AsyncSession = Depends(get_db)):
    stmt = select(User).where(User.username == user.username)
    result = await db.execute(stmt)
    if result.scalar_one_or_none():
        raise HTTPException(status_code=400, detail="Username already registered")
        
    hashed_password = get_password_hash(user.password)
    db_user = User(username=user.username, email=user.email, hashed_password=hashed_password)
    db.add(db_user)
    await db.commit()
    await db.refresh(db_user)
    return db_user

@router.post("/login", response_model=Token)
async def login(form_data: OAuth2PasswordRequestForm = Depends(), db: AsyncSession = Depends(get_db)):
    from sqlalchemy.orm import selectinload
    stmt = select(User).options(selectinload(User.roles)).where(User.username == form_data.username)
    result = await db.execute(stmt)
    user = result.scalar_one_or_none()
    
    if not user or not verify_password(form_data.password, user.hashed_password):
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Incorrect username or password",
            headers={"WWW-Authenticate": "Bearer"},
        )
        

        
    # MFA check
    stmt_mfa = select(SystemSetting).where(SystemSetting.key.in_(["mfa_enabled", "mfa_config"])).where(SystemSetting.company_id == user.company_id)
    result_mfa = await db.execute(stmt_mfa)
    settings = result_mfa.scalars().all()
    
    mfa_enabled = False
    mfa_config = {}
    for s in settings:
        if s.key == "mfa_enabled" and s.value == "true":
            mfa_enabled = True
        elif s.key == "mfa_config" and s.value:
            try:
                mfa_config = json.loads(s.value)
            except:
                pass
                
    if mfa_enabled and user.email:
        # Generate code
        code = generate_mfa_code()
        user.mfa_code = code
        user.mfa_code_expires = datetime.utcnow() + timedelta(minutes=5)
        await db.commit()
        
        # Send email
        host = mfa_config.get("smtp_host")
        port = mfa_config.get("smtp_port")
        smtp_user = mfa_config.get("smtp_user")
        smtp_pass = mfa_config.get("smtp_password")
        
        sent = False
        if host and port and smtp_user and smtp_pass:
            sent = await send_mfa_email(host, port, smtp_user, smtp_pass, user.email, code)
            
        if not sent:
            from sqlalchemy.orm import selectinload
            stmt_roles = select(User).options(selectinload(User.roles)).where(User.id == user.id)
            res_roles = await db.execute(stmt_roles)
            user_with_roles = res_roles.scalar_one()
            
            if any(r.name == "Administrador" for r in user_with_roles.roles):
                # Fallback de emergencia para Administradores
                access_token = create_access_token(data={"sub": user.username, "roles": [r.name for r in user_with_roles.roles]})
                audit = AuditLog(user_id=user.id, username=user.username, action="Inicio de sesión (Fallback MFA)")
                db.add(audit)
                await db.commit()
                return {"access_token": access_token, "token_type": "bearer", "mfa_required": False, "mfa_token": None}
            else:
                raise HTTPException(status_code=500, detail="Fallo al enviar el código MFA. Contacte al administrador del sistema.")
            
        mfa_token = create_access_token(data={"sub": user.username, "mfa_pending": True})
        return {"access_token": None, "token_type": None, "mfa_required": True, "mfa_token": mfa_token}
        
    roles = [r.name for r in user.roles] if hasattr(user, 'roles') else []
    access_token = create_access_token(data={"sub": user.username, "roles": roles})
    
    # Registrar log de auditoría
    audit = AuditLog(user_id=user.id, username=user.username, action="Inicio de sesión exitoso")
    db.add(audit)
    await db.commit()
    
    return {"access_token": access_token, "token_type": "bearer", "mfa_required": False, "mfa_token": None}

@router.post("/verify-mfa", response_model=Token)
async def verify_mfa(mfa_data: MFAVerify, db: AsyncSession = Depends(get_db)):
    from auth import SECRET_KEY, ALGORITHM
    try:
        payload = jwt.decode(mfa_data.mfa_token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        mfa_pending: bool = payload.get("mfa_pending")
        if username is None or not mfa_pending:
            raise HTTPException(status_code=401, detail="Token inválido")
    except jwt.PyJWTError:
        raise HTTPException(status_code=401, detail="Token inválido o expirado")
        
    from sqlalchemy.orm import selectinload
    stmt = select(User).options(selectinload(User.roles)).where(User.username == username)
    result = await db.execute(stmt)
    user = result.scalar_one_or_none()
    
    if not user:
        raise HTTPException(status_code=401, detail="Usuario no encontrado")
        
    if not user.mfa_code or user.mfa_code != mfa_data.code:
        raise HTTPException(status_code=400, detail="Código MFA incorrecto")
        
    if not user.mfa_code_expires or user.mfa_code_expires < datetime.utcnow():
        raise HTTPException(status_code=400, detail="Código MFA expirado")
        
    # Valid
    user.mfa_code = None
    user.mfa_code_expires = None
    
    roles = [r.name for r in user.roles] if hasattr(user, 'roles') else []
    access_token = create_access_token(data={"sub": user.username, "roles": roles})
    
    audit = AuditLog(user_id=user.id, username=user.username, action="Inicio de sesión exitoso (MFA)")
    db.add(audit)
    await db.commit()
    
    return {"access_token": access_token, "token_type": "bearer", "mfa_required": False, "mfa_token": None}

@router.put("/password")
async def change_password(
    pwd_data: PasswordChange,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_active_user)
):
    if not verify_password(pwd_data.current_password, current_user.hashed_password):
        raise HTTPException(status_code=400, detail="La contraseña actual es incorrecta")
        
    current_user.hashed_password = get_password_hash(pwd_data.new_password)
    await db.commit()
    return {"status": "success", "message": "Contraseña actualizada correctamente"}
