"""Comptes : connexion, inscription, déconnexion et paramètres."""
import re
from urllib.parse import urlsplit

from flask import Blueprint, current_app, flash, redirect, render_template, request, url_for
from flask_login import current_user, login_required, login_user, logout_user
from sqlalchemy.exc import IntegrityError

from models import db, User

auth_bp = Blueprint('auth', __name__)

AGENCIES = ['PADI', 'SSI', 'CMAS', 'NAUI', 'SDI', 'TDI', 'RAID', 'BSAC', 'Autre']
EMAIL_RE = re.compile(r'^[^@\s]+@[^@\s]+\.[^@\s]+$')
MIN_PASSWORD = 8


def _safe_next(target):
    """Chemin relatif du site, sinon None : jamais de redirection vers un autre domaine."""
    if not target or not target.startswith('/') or target.startswith('//') or '\\' in target:
        return None
    # Les navigateurs ignorent tabulations et retours à la ligne : « /\t/x.com » devient « //x.com ».
    if any(ord(c) <= 32 or ord(c) == 127 for c in target):
        return None
    parts = urlsplit(target)
    return None if parts.scheme or parts.netloc else target


def _home():
    return url_for('training.dashboard')


@auth_bp.route('/connexion', methods=['GET', 'POST'])
def login():
    next_url = _safe_next(request.values.get('next'))
    if current_user.is_authenticated:
        return redirect(next_url or _home())
    if request.method == 'POST':
        email = (request.form.get('email') or '').strip().lower()
        user = User.query.filter_by(email=email).first()
        if not user or not user.check_password(request.form.get('password') or ''):
            flash('Courriel ou mot de passe invalide.', 'error')
        elif not user.is_active:
            flash('Ce compte est désactivé.', 'error')
        else:
            login_user(user, remember=bool(request.form.get('remember')))
            return redirect(next_url or _home())
    return render_template('auth/login.html', next_url=next_url)


@auth_bp.route('/inscription', methods=['GET', 'POST'])
def register():
    if not current_app.config['ALLOW_REGISTRATION']:
        flash('Les inscriptions sont fermées.', 'error')
        return redirect(url_for('auth.login'))
    if current_user.is_authenticated:
        return redirect(_home())
    if request.method == 'POST':
        email = (request.form.get('email') or '').strip().lower()
        password = request.form.get('password') or ''
        error = None
        if not EMAIL_RE.match(email) or len(email) > 120:
            error = 'Courriel invalide.'
        elif len(password) < MIN_PASSWORD:
            error = f'Le mot de passe doit contenir au moins {MIN_PASSWORD} caractères.'
        elif password != request.form.get('confirm'):
            error = 'Les deux mots de passe ne correspondent pas.'
        elif User.query.filter_by(email=email).first():
            error = 'Ce courriel est déjà utilisé.'
        if error:
            flash(error, 'error')
            return render_template('auth/register.html')
        user = User(email=email, certification_agency='PADI',
                    first_name=(request.form.get('first_name') or '').strip()[:50] or None,
                    last_name=(request.form.get('last_name') or '').strip()[:50] or None)
        user.set_password(password)
        db.session.add(user)
        try:
            db.session.commit()
        except IntegrityError:  # inscription simultanée avec le même courriel
            db.session.rollback()
            flash('Ce courriel est déjà utilisé.', 'error')
            return render_template('auth/register.html')
        login_user(user)
        flash('Bienvenue ! Indiquez votre n° PADI dans les paramètres si vous êtes instructeur.', 'success')
        return redirect(_home())
    return render_template('auth/register.html')


@auth_bp.route('/deconnexion', methods=['POST'])
@login_required
def logout():
    logout_user()
    flash('Vous êtes déconnecté.', 'info')
    return redirect(url_for('auth.login'))


@auth_bp.route('/parametres', methods=['GET', 'POST'])
@login_required
def settings():
    if request.method == 'POST':
        if request.form.get('form') == 'password':
            new = request.form.get('new_password') or ''
            if not current_user.check_password(request.form.get('current_password') or ''):
                flash('Mot de passe actuel incorrect.', 'error')
            elif len(new) < MIN_PASSWORD:
                flash(f'Le nouveau mot de passe doit contenir au moins {MIN_PASSWORD} caractères.', 'error')
            elif new != request.form.get('confirm'):
                flash('Les deux mots de passe ne correspondent pas.', 'error')
            else:
                current_user.set_password(new)
                db.session.commit()
                flash('Mot de passe modifié.', 'success')
        else:
            agency = request.form.get('certification_agency')
            current_user.first_name = (request.form.get('first_name') or '').strip()[:50] or None
            current_user.last_name = (request.form.get('last_name') or '').strip()[:50] or None
            current_user.certification_agency = agency if agency in AGENCIES else 'PADI'
            current_user.certification_number = (request.form.get('certification_number') or '').strip()[:50] or None
            db.session.commit()
            flash('Profil mis à jour.', 'success')
        return redirect(url_for('auth.settings'))
    return render_template('settings.html', agencies=AGENCIES)
