import os
import re
import random
import secrets
import logging
from datetime import datetime, timedelta
from flask import current_app, render_template_string
from app.extensions import db
from app.models.user import User
from app.models.notification import Notification
from app.models.verification import EmailVerification

logger = logging.getLogger(__name__)


def generate_otp(length=6):
    return "".join(str(random.randint(0, 9)) for _ in range(length))


def send_email(user, subject, html_body, text_body=""):
    """
    Dev-mode email sender: writes to log and returns the code.
    In production, replace with SMTP/mail service.
    """
    code = None
    # Try to find OTP code in context if it's a verification email
    if "code" in html_body:
        m = re.search(r">(\d{4,8})<", html_body)
        if m:
            code = m.group(1)
    logger.warning("[DEV EMAIL] To=%s Subject=%s Code=%s Body preview=%s",
                   user.email, subject, code, html_body[:200])
    return code


def signup_user(email, password, username=None):
    if User.query.filter_by(email=email).first():
        raise ValueError("Email already registered")
    user = User(
        email=email.lower(),
        username=username or email.split("@")[0],
    )
    user.set_password(password)
    db.session.add(user)
    db.session.flush()
    user.numeric_id = 100000 + user.id
    # Create wallets
    from app.models.wallet import Wallet
    db.session.add(Wallet(user_id=user.id, kind="deposit", balance=0))
    db.session.add(Wallet(user_id=user.id, kind="payout", balance=0))
    db.session.flush()
    verification = EmailVerification.create(user.id, purpose="signup")
    db.session.commit()
    return user, verification.code


def verify_email(user_id, code):
    record = EmailVerification.query.filter_by(
        user_id=user_id, code=code, used=False
    ).order_by(EmailVerification.created_at.desc()).first()
    if not record:
        raise ValueError("Invalid or expired code")
    if record.expires_at < datetime.utcnow():
        raise ValueError("Code expired")
    user = User.query.get(user_id)
    user.email_verified = True
    record.used = True
    db.session.commit()
    return user


def login_user(email, password):
    user = User.query.filter_by(email=email.lower()).first()
    if not user or not user.check_password(password):
        return None, "Invalid credentials"
    if not user.is_active:
        return None, "Account deactivated"
    if not user.email_verified:
        return None, "Please verify your email first"
    user.last_login = datetime.utcnow()
    db.session.commit()
    return user, None


def change_language(user_id, lang_code):
    user = User.query.get(user_id)
    if user:
        user.lang = lang_code
        db.session.commit()


def get_locale_lang(user):
    if user and user.lang:
        return user.lang
    return current_app.config.get("DEFAULT_LANG", "en")


def create_password_reset_token(user_id):
    """Generate a single-use reset token for a user (dev: 8 digits)."""
    user = User.query.get(user_id)
    if not user:
        return None, None
    user.reset_token = secrets.token_urlsafe(24)
    user.reset_token_expiry = datetime.utcnow() + timedelta(minutes=30)
    db.session.commit()
    # Dev-mode short numeric code (mirrors OTP flow for consistency in demo)
    code = "".join(str(random.randint(0, 9)) for _ in range(8))
    db.session.flush()
    return user.reset_token, code


def reset_password(user_id, token, new_password):
    """Validate token + expiry, then set new password."""
    user = User.query.get(user_id)
    if not user:
        return None, "User not found"
    if not user.reset_token or user.reset_token != token:
        return None, "Invalid reset token"
    if not user.reset_token_expiry or user.reset_token_expiry < datetime.utcnow():
        return None, "Reset code has expired"
    if len(new_password) < 6:
        return None, "Password must be at least 6 characters"
    user.set_password(new_password)
    user.reset_token = None
    user.reset_token_expiry = None
    db.session.commit()
    return user, None


def request_password_reset(email):
    """Find user by email, issue a reset token + dev code. Returns (user, code_or_None)."""
    user = User.query.filter_by(email=email.lower()).first()
    if not user:
        return None, None  # don't reveal whether user exists
    token, code = create_password_reset_token(user.id)
    # notify via email (dev logging) + in-app notification
    try:
        send_email(user, "Password Reset", f"Reset code: {code}\n\nThis code expires in 30 minutes.")
    except Exception:
        pass
    db.session.add(Notification(
        user_id=user.id, type="reset",
        title="Password Reset Requested",
        body=f"Your 8-digit reset code: {code} (expires in 30 minutes).",
    ))
    db.session.commit()
    return user, code
