"""
Authentication API Routes
"""
from datetime import datetime, timedelta
from fastapi import APIRouter, HTTPException, Depends, status, Request, BackgroundTasks
from pydantic import BaseModel, EmailStr, validator
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
import secrets
import re

from app.database import get_db
from app.models.user import User, UserRole, UserSession
from app.models.client import Client, ClientAPIKey, ClientStatus
from app.auth.password import hash_password, verify_password
from app.auth.jwt_handler import (
    create_access_token,
    create_refresh_token,
    verify_token,
    get_token_data,
    get_current_user,
    get_current_active_user
)
from app.services.email_service import email_service

router = APIRouter()


# ========================
# Request/Response Models
# ========================

class LoginRequest(BaseModel):
    email: EmailStr
    password: str


class RegisterRequest(BaseModel):
    email: EmailStr
    password: str
    first_name: str
    last_name: str = None
    phone: str = None
    company_name: str = None


class TokenResponse(BaseModel):
    access_token: str
    refresh_token: str
    token_type: str = "bearer"
    expires_in: int


class RefreshTokenRequest(BaseModel):
    refresh_token: str


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


class ForgotPasswordRequest(BaseModel):
    email: EmailStr


class ResetPasswordRequest(BaseModel):
    token: str
    new_password: str

    @validator('new_password')
    def validate_password(cls, v):
        if len(v) < 8:
            raise ValueError('Password must be at least 8 characters long')
        if not re.search(r'[A-Z]', v):
            raise ValueError('Password must contain at least one uppercase letter')
        if not re.search(r'[a-z]', v):
            raise ValueError('Password must contain at least one lowercase letter')
        if not re.search(r'\d', v):
            raise ValueError('Password must contain at least one digit')
        return v


class VerifyEmailRequest(BaseModel):
    token: str


class UserResponse(BaseModel):
    id: int
    email: str
    first_name: str
    last_name: str = None
    role: str
    client_id: int = None
    is_verified: bool

    class Config:
        from_attributes = True


# ========================
# Authentication Endpoints
# ========================

@router.post("/login", response_model=TokenResponse)
async def login(
        request: LoginRequest,
        req: Request,
        db: AsyncSession = Depends(get_db)
):
    """
    Login with email and password
    """
    # Find user
    result = await db.execute(
        select(User).where(User.email == request.email)
    )
    user = result.scalar_one_or_none()

    if not user or not verify_password(request.password, user.password_hash):
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Incorrect email or password"
        )

    if not user.is_active:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Account is inactive"
        )

    # Update last login
    user.last_login_at = datetime.utcnow()
    user.last_login_ip = req.client.host if req.client else None
    user.failed_login_attempts = 0
    await db.commit()

    # Create tokens
    token_data = get_token_data(user)
    access_token = create_access_token(token_data)
    refresh_token = create_refresh_token(token_data)

    return TokenResponse(
        access_token=access_token,
        refresh_token=refresh_token,
        expires_in=1800  # 30 minutes
    )


@router.post("/register", response_model=UserResponse)
async def register(
        request: RegisterRequest,
        db: AsyncSession = Depends(get_db)
):
    """
    Register a new user and create client account
    """
    # Check if email exists
    result = await db.execute(
        select(User).where(User.email == request.email)
    )
    if result.scalar_one_or_none():
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Email already registered"
        )

    # Create client account if company_name provided
    client = None
    if request.company_name:
        client = Client(
            company_name=request.company_name,
            company_email=request.email,
            contact_name=f"{request.first_name} {request.last_name or ''}".strip(),
            contact_email=request.email,
            contact_phone=request.phone,
            status=ClientStatus.TRIAL,
            trial_ends_at=datetime.utcnow() + timedelta(days=14)
        )
        db.add(client)
        await db.flush()

    # Create user
    user = User(
        email=request.email,
        password_hash=hash_password(request.password),
        first_name=request.first_name,
        last_name=request.last_name,
        phone=request.phone,
        client_id=client.id if client else None,
        role=UserRole.CLIENT_OWNER if client else UserRole.CLIENT_USER,
        email_verification_token=secrets.token_urlsafe(32)
    )
    db.add(user)
    await db.commit()
    await db.refresh(user)

    # TODO: Send verification email

    return UserResponse(
        id=user.id,
        email=user.email,
        first_name=user.first_name,
        last_name=user.last_name,
        role=user.role.value,
        client_id=user.client_id,
        is_verified=user.is_verified
    )


@router.post("/refresh", response_model=TokenResponse)
async def refresh_token(
        request: RefreshTokenRequest,
        db: AsyncSession = Depends(get_db)
):
    """
    Refresh access token using refresh token
    """
    payload = verify_token(request.refresh_token, "refresh")
    if not payload:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid or expired refresh token"
        )

    user_id = payload.get("sub")
    result = await db.execute(select(User).where(User.id == int(user_id)))
    user = result.scalar_one_or_none()

    if not user or not user.is_active:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="User not found or inactive"
        )

    token_data = get_token_data(user)
    access_token = create_access_token(token_data)
    new_refresh_token = create_refresh_token(token_data)

    return TokenResponse(
        access_token=access_token,
        refresh_token=new_refresh_token,
        expires_in=1800
    )


@router.get("/me", response_model=UserResponse)
async def get_me(current_user: User = Depends(get_current_active_user)):
    """
    Get current user profile
    """
    return UserResponse(
        id=current_user.id,
        email=current_user.email,
        first_name=current_user.first_name,
        last_name=current_user.last_name,
        role=current_user.role.value,
        client_id=current_user.client_id,
        is_verified=current_user.is_verified
    )


@router.post("/change-password")
async def change_password(
        request: ChangePasswordRequest,
        current_user: User = Depends(get_current_active_user),
        db: AsyncSession = Depends(get_db)
):
    """
    Change user password
    """
    if not verify_password(request.current_password, current_user.password_hash):
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Current password is incorrect"
        )

    current_user.password_hash = hash_password(request.new_password)
    await db.commit()

    return {"message": "Password changed successfully"}


@router.post("/forgot-password")
async def forgot_password(
        request: ForgotPasswordRequest,
        background_tasks: BackgroundTasks,
        db: AsyncSession = Depends(get_db)
):
    """
    Request password reset email

    Sends a password reset link to the user's email if the account exists.
    For security, always returns success even if email doesn't exist.
    """
    result = await db.execute(
        select(User).where(User.email == request.email)
    )
    user = result.scalar_one_or_none()

    if user:
        # Generate reset token
        reset_token = secrets.token_urlsafe(32)
        user.password_reset_token = reset_token
        user.password_reset_expires = datetime.utcnow() + timedelta(hours=1)
        await db.commit()

        # Send reset email in background
        background_tasks.add_task(
            email_service.send_password_reset_email,
            to_email=user.email,
            user_name=user.first_name or "User",
            reset_token=reset_token
        )

    # Always return success to prevent email enumeration
    return {
        "message": "If an account with that email exists, we've sent a password reset link.",
        "success": True
    }


@router.post("/reset-password")
async def reset_password(
        request: ResetPasswordRequest,
        background_tasks: BackgroundTasks,
        db: AsyncSession = Depends(get_db)
):
    """
    Reset password using token

    Validates the reset token and updates the user's password.
    Sends a confirmation email after successful reset.
    """
    result = await db.execute(
        select(User).where(
            User.password_reset_token == request.token,
            User.password_reset_expires > datetime.utcnow()
        )
    )
    user = result.scalar_one_or_none()

    if not user:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Invalid or expired reset token. Please request a new password reset."
        )

    # Update password
    user.password_hash = hash_password(request.new_password)
    user.password_reset_token = None
    user.password_reset_expires = None
    user.failed_login_attempts = 0  # Reset failed attempts
    user.locked_until = None  # Unlock account if locked
    await db.commit()

    # Send confirmation email in background
    background_tasks.add_task(
        email_service.send_password_changed_email,
        to_email=user.email,
        user_name=user.first_name or "User"
    )

    return {
        "message": "Password reset successfully. You can now login with your new password.",
        "success": True
    }


@router.post("/verify-email")
async def verify_email(
        request: VerifyEmailRequest,
        db: AsyncSession = Depends(get_db)
):
    """
    Verify email address using token
    """
    result = await db.execute(
        select(User).where(User.email_verification_token == request.token)
    )
    user = result.scalar_one_or_none()

    if not user:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Invalid verification token"
        )

    if user.is_verified:
        return {"message": "Email already verified", "success": True}

    user.is_verified = True
    user.email_verified_at = datetime.utcnow()
    user.email_verification_token = None
    await db.commit()

    return {"message": "Email verified successfully", "success": True}


@router.post("/resend-verification")
async def resend_verification(
        request: ForgotPasswordRequest,  # Reuse same model (just needs email)
        background_tasks: BackgroundTasks,
        db: AsyncSession = Depends(get_db)
):
    """
    Resend email verification link
    """
    result = await db.execute(
        select(User).where(User.email == request.email)
    )
    user = result.scalar_one_or_none()

    if user and not user.is_verified:
        # Generate new verification token
        verification_token = secrets.token_urlsafe(32)
        user.email_verification_token = verification_token
        await db.commit()

        # Send verification email in background
        background_tasks.add_task(
            email_service.send_verification_email,
            to_email=user.email,
            user_name=user.first_name or "User",
            verification_token=verification_token
        )

    return {
        "message": "If the email exists and is not verified, we've sent a verification link.",
        "success": True
    }


@router.get("/validate-reset-token/{token}")
async def validate_reset_token(
        token: str,
        db: AsyncSession = Depends(get_db)
):
    """
    Validate if a password reset token is still valid

    Use this endpoint before showing the reset password form
    to check if the token is valid and not expired.
    """
    result = await db.execute(
        select(User).where(
            User.password_reset_token == token,
            User.password_reset_expires > datetime.utcnow()
        )
    )
    user = result.scalar_one_or_none()

    if not user:
        return {
            "valid": False,
            "message": "Invalid or expired reset token"
        }

    return {
        "valid": True,
        "email": user.email[:3] + "***" + user.email[user.email.index("@"):],  # Masked email
        "expires_in_minutes": int((user.password_reset_expires - datetime.utcnow()).total_seconds() / 60)
    }


@router.post("/logout")
async def logout(current_user: User = Depends(get_current_user)):
    """
    Logout (client should discard tokens)
    """
    # In a production system, you might want to blacklist the token
    return {"message": "Logged out successfully"}
