from pathlib import Path
from typing import Annotated, Optional
from fastapi.responses import JSONResponse
from pydantic import EmailStr
from utility.auth_token import get_current_user
from core.generate_otp import generate_otp
from datetime import datetime, timedelta
from core.excption import NotFoundException
from schemas.users import CompleteSignupRequest, ForgotPasswordRequest, LoginSchema, PreSignupRequest, ResetPasswordRequest, UpdatePassword, UserUpdate, VerifyOtpRequest
from sqlalchemy.orm import Session
from models.models import Loan, LoanStatus, User, Country, Wallet, TimAccountType
from core.hash_password import Hasher
from fastapi import  BackgroundTasks, Form, status,HTTPException, Depends, Request, logger
from core.config import settings
from database import get_db
from models.models import User, Country, City
import uuid
from utility.auth_token import create_access_token,create_refresh_token
from fastapi import HTTPException, UploadFile, File
from sqlalchemy.orm import Session
from fastapi.encoders import jsonable_encoder
from fastapi.responses import JSONResponse

# Fonction pour obtenir la limite de compte selon le type
def get_account_limit(account_type):
    """Obtenir la limite de solde selon le type de compte TIM"""
    if isinstance(account_type, str):
        # Convertir string en enum si nécessaire
        try:
            account_type = TimAccountType[account_type]
        except:
            return 2000000.0  # Par défaut
    
    limits = {
        TimAccountType.TIM_MINI: 2000000.0,      # 2M FCFA
        TimAccountType.TIM_MAXI: 2000000.0,      # 2M FCFA
        TimAccountType.TIM_BUSINESS: None        # Illimité
    }
    return limits.get(account_type, 2000000.0)
import os, shutil
from  utility.send_email import  send_mail

UPLOAD_DIR = Path("uploads")
UPLOAD_DIR.mkdir(parents=True, exist_ok=True)

otp_storage = {} 

async def pre_signup(payload: PreSignupRequest, db: Session, background_tasks: BackgroundTasks):
    # Vérifier le pays et la ville
    country = db.query(Country).filter(Country.id == payload.country_id).first()
    if not country:
        raise HTTPException(status_code=404, detail="Country not found")

    city = db.query(City).filter(
        City.id == payload.city_id,
        City.country_id == payload.country_id
    ).first()
    if not city:
        raise HTTPException(status_code=404, detail="City not found or does not belong to the country")

    user = db.query(User).filter(User.email == payload.email).first()

    otp = await generate_otp(4)

    try:
        # Cas 1 : email déjà enregistré mais user pas activé => renvoyer OTP
        if user and user.is_active is False:
            user.otp_secret = otp
            db.commit()
            background_tasks.add_task(send_mail, payload.email, otp)
            data = {"success": True, "message": "OTP envoyé avec succès", "email": payload.email}
            json_data = jsonable_encoder(data)
        
            return JSONResponse(
                content=json_data,  
                media_type="application/json; charset=utf-8"
            )

        # Cas 2 : email déjà activé => refuser inscription
        if user and user.is_active is True:
            raise HTTPException(status_code=400, detail="Email already registered")

        # Cas 3 : user inexistant => créer le compte
        new_user = User(
            id=str(uuid.uuid4()),
            email=payload.email,
            country_id=payload.country_id,
            is_active=False,
            otp_secret=otp
        )
        db.add(new_user)
        db.commit()
        background_tasks.add_task(send_mail, payload.email, otp)
        data = {"success": True, "message": "OTP envoyé avec succès", "email": payload.email}
        json_data = jsonable_encoder(data)
      
        return JSONResponse(
            content=json_data,  
            media_type="application/json; charset=utf-8"
        )

    except Exception as e:
        db.rollback()
        raise HTTPException(status_code=500, detail=f"Erreur : {e}")

# ----------------------
# Phase 1 : Vérification OTP
# -------------------
async def verify_is_opt(request: VerifyOtpRequest, db: Session):
    user = db.query(User).filter(User.email == request.email, User.otp_secret == request.otp).first()
    if not user:
        raise NotFoundException(detail="User not found")
    user.is_active = False
    user.otp_secret = None
    db.add(user)
    db.commit()
    db.refresh(user)
    return user
# ----------------------
# Phase 2 : Compléter le profil
# ----------------------

def complete_signup(payload: CompleteSignupRequest, db: Session = Depends(get_db)):
    # Vérifier OTP (optionnel si tu veux sécuriser)
    user_in_db = db.query(User).filter(User.email == payload.email).first()
    print(payload)
    if not user_in_db:
        raise HTTPException(status_code=404, detail=f"User with email {payload.email} not found")

    hashed_password = Hasher.get_password_hash(payload.password)
    
    user_in_db.full_name = payload.full_name
    user_in_db.last_name = payload.last_name
    user_in_db.email = payload.email
    user_in_db.phone = payload.phone
    user_in_db.hashed_password = hashed_password
    user_in_db.is_active = True
    user_in_db.tim_account_type = payload.tim_account_type
    
    db.add(user_in_db)
    db.commit()
    db.refresh(user_in_db)
    
    wallet = Wallet(
        id=str(uuid.uuid4()),
        user_id=user_in_db.id,
        balance=0.0,
        max_balance=get_account_limit(user_in_db.tim_account_type)
    )

    db.add(wallet)
    db.commit()

    return {"success": True, "message": "User registered successfully"}


async def login_user(user_credentials: LoginSchema, db: Session = Depends(get_db)):
    print(f"\n🔐 Login attempt:")
    print(f"   Phone/Email: {user_credentials.phone}")
    print(f"   Password received: {user_credentials.hashed_password[:10]}... (length: {len(user_credentials.hashed_password)})")
    print(f"   Country ID: {user_credentials.country_id}")
    
    # Chercher par téléphone OU email
    user = db.query(User).filter(
        (User.phone == user_credentials.phone) | (User.email == user_credentials.phone)
    ).first()

    if not user:
        print(f"   ❌ User not found with phone/email: {user_credentials.phone}")
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials")
    
    print(f"   ✅ User found: {user.email}")
    print(f"   Stored hash: {user.hashed_password[:50]}...")
    
    # Corrected password verification
    is_valid = Hasher.verify_password(user_credentials.hashed_password, user.hashed_password)
    print(f"   Password valid: {is_valid}")
    
    if not is_valid:
        print(f"   ❌ Password verification failed!")
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials")

    if not user.is_active:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Account disabled")

    access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
    access_token = create_access_token(
        data={"sub": user.email, "account_type": user.tim_account_type.name if user.tim_account_type else "TIM_MAXI"}, expires_delta=access_token_expires
    )
    refresh_token = create_refresh_token(data={"sub": user.email})
    
    return {
        "success": True,
        "data": {
            "access_token": access_token,
            "refresh_token": refresh_token,
            "token_type": "Bearer"
        }
        
    }
def get_user_wallet(user_id: str, db: Session):
    wallet = db.query(Wallet).filter(Wallet.user_id == user_id).first()
    if not wallet:
        wallet = Wallet(
            id=str(uuid.uuid4()),
            user_id=user_id,
            balance=0.0,
            max_balance=get_account_limit("TIM_MINI")  # ou selon le type de compte
        )
        db.add(wallet)
        db.commit()
        db.refresh(wallet)
    return wallet



UPLOAD_DIR = Path("uploads")
UPLOAD_DIR.mkdir(parents=True, exist_ok=True)

async def update_user_profile(
    email: Optional[str] = Form(None),
    full_name: Optional[str] = Form(None),
    last_name: Optional[str] = Form(None),
    phone: Optional[str] = Form(None),
    file: Optional[UploadFile] = File(None),
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    user_in_db = db.query(User).filter(User.id == current_user.id).first()
    if not user_in_db:
        raise HTTPException(status_code=404, detail="Compte introuvable")

    # Passe les valeurs réelles
    if email is not None:
        user_in_db.email = email
    if full_name is not None:
        user_in_db.full_name = full_name
    if phone is not None:
        user_in_db.phone = phone
    if last_name is not None:
        user_in_db.last_name = last_name

    # Gestion du fichier
    if file:
        content = await file.read()
        extension = file.filename.split(".")[-1]
        unique_filename = f"{uuid.uuid4()}.{extension}"
        file_path = UPLOAD_DIR / unique_filename

        with open(file_path, "wb") as f:
            f.write(content)

        #  Mettre l'URL ou path selon ton modèle
        user_in_db.photo_profile = f"{settings.APP_BASE_URL}/uploads/{unique_filename}"

    db.commit()
    db.refresh(user_in_db)

    return {
        "message": "Profil mis à jour avec succès",
        "user": {
            "id": user_in_db.id,
            "username": user_in_db.username,
            "full_name": user_in_db.full_name,
            "email": user_in_db.email,
            "phone": user_in_db.phone,
            "profile_photo": user_in_db.photo_profile
        }
    }
    
async def mot_depasse_oublie(request: ForgotPasswordRequest, db: Session, background_tasks: BackgroundTasks):
    user = db.query(User).filter(User.email == request.email).first()
    if not user:
        raise HTTPException(status_code=404, detail="Utilisateur non trouvé")
    
    otp = await generate_otp(4)
    user.otp_secret = otp
    db.add(user)
    db.commit()
    db.refresh(user)
    
    print(f"OTP pour {request.email}: {otp}")
    background_tasks.add_task(send_mail, request.email, otp)
    
    return {"success": True, "message": "OTP envoyé à l'email", "email": request.email}

async def reset_password(curent_data:ResetPasswordRequest,  db: Session):
    user = db.query(User).filter(User.email == curent_data.email, User.otp_secret == curent_data.confirmationCode).first()
    if not user:
        raise HTTPException(status_code=404, detail="Utilisateur non trouvé ou OTP invalide")   
    hashed_password = Hasher.get_password_hash(curent_data.password)
    user.hashed_password = hashed_password  
    user.otp_secret = None  
    db.add(user)    
    db.commit()
    db.refresh(user)   
    return {"success": True, "message": "Mot de passe réinitialisé avec succès"}


async def blocked_user(user_id: str, db: Session):
    user_in_db = db.query(User).filter(User.id == user_id).first()
    if not user_in_db:
        raise HTTPException(status_code=404, detail="Utilisateur non trouvé")
    
    user_in_db.is_active = False
    db.add(user_in_db)
    db.commit()
    db.refresh(user_in_db)
    
    return {"success": True, "message": "Utilisateur bloqué avec succès"}   


async def active_user(user_id: str, db: Session):
    user_in_db = db.query(User).filter(User.id == user_id).first()
    if not user_in_db:
        raise HTTPException(status_code=404, detail="Utilisateur non trouvé")
    
    user_in_db.is_active = True
    db.add(user_in_db)
    db.commit()
    db.refresh(user_in_db)
    
    return {"success": True, "message": "Utilisateur bloqué avec succès"}   
from datetime import datetime

async def delete_account(current_user: User, db: Session):
    user = db.query(User).filter(User.id == current_user.id).first()

    if not user:
        raise HTTPException(404, "Utilisateur non trouvé")

    # ❌ Prêt actif → refus
    active_loan = db.query(Loan).filter(
        Loan.user_id == user.id,
        Loan.status == LoanStatus.APPROVED
    ).first()

    if active_loan:
        raise HTTPException(
            status_code=400,
            detail="Vous ne pouvez pas supprimer votre compte avec un prêt actif"
        )

    # 🔒 Vérifier solde
    if user.wallet.balance > 0:
        raise HTTPException(
            status_code=400,
            detail="Veuillez retirer votre solde avant suppression"
        )

    # 🔐 Désactivation + anonymisation
    user.is_active = False

    user.email = f"deleted_{user.id}@timcash.local"
    user.phone = None
    user.full_name = None
    user.photo_profile = None
    user.hashed_password = None

    db.commit()

    return {
        "success": True,
        "message": "Compte désactivé définitivement"
    }

async def update_password(
    current_user: User,
    data_password: UpdatePassword,
    db: Session
):
    user_in_db = db.query(User).filter(
        User.id == current_user.id
    ).first()

    if not user_in_db:
        raise HTTPException(404, "Utilisateur non trouvé")

    if not Hasher.verify_password(
        data_password.old_password,
        user_in_db.hashed_password
    ):
        raise HTTPException(400, "Ancien mot de passe incorrect")

    if Hasher.verify_password(
        data_password.new_password,
        user_in_db.hashed_password
    ):
        raise HTTPException(
            400,
            "Le nouveau mot de passe ne peut pas être identique à l'ancien"
        )

    if data_password.new_password != data_password.confirm_new_password:
        raise HTTPException(
            400,
            "Les nouveaux mots de passe ne correspondent pas"
        )

    user_in_db.hashed_password = Hasher.get_password_hash(
        data_password.new_password
    )

    db.commit()
    db.refresh(user_in_db)
    return {
        "success": True,
        "message": "Mot de passe mis à jour avec succès"
    }