import os
import msal
import uuid
import logging

from src.extensions import db
from src.security import limiter
from src.models.user import User
from src.security.validators import validate_user_login_data
from src.security.audit import audit_route, audit_auth_attempt
from src.discord.discord_webhook import send_discord_audit_log
from flask_login import login_user, logout_user, login_required
from flask import Blueprint, render_template, request, redirect, url_for, flash, jsonify, session
from flask_jwt_extended import create_access_token, create_refresh_token, jwt_required, get_jwt_identity

CLIENT_ID = os.environ.get("SHAREPOINT_CLIENT_ID")
CLIENT_SECRET = os.environ.get("SHAREPOINT_CLIENT_SECRET")
AUTHORITY = f"https://login.microsoftonline.com/{os.environ.get('SHAREPOINT_TENANT_ID')}"
REDIRECT_PATH = "/getAToken"
SCOPE = ["User.Read", "Files.Read", "Sites.Read.All"]

print('CLIENT_ID:', CLIENT_ID)
print('CLIENT_SECRET:', 'SET' if CLIENT_SECRET else 'NOT SET')
print('AUTHORITY:', AUTHORITY)
print('REDIRECT_PATH:', REDIRECT_PATH)
print('SCOPE:', SCOPE)

auth = Blueprint('auth', __name__)

def _build_msal_app(cache=None):
    return msal.ConfidentialClientApplication(
        CLIENT_ID, authority=AUTHORITY,
        client_credential=CLIENT_SECRET, token_cache=cache)

@auth.route('/login', methods=['GET', 'POST'])
@limiter.limit("5 per minute")
@audit_route
def login():
    """
    Endpoint de login.
    ---
    tags:
      - auth
    summary: Autentica um usuário
    description: Permite que um usuário faça login no sistema
    parameters:
      - name: email
        in: formData
        type: string
        required: true
        description: E-mail do usuário
      - name: password
        in: formData
        type: string
        required: true
        description: Senha do usuário
    responses:
      200:
        description: Login bem-sucedido
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Success'
      401:
        description: Credenciais inválidas
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Error'
    """
    if request.method == 'POST':
        try:
            data = validate_user_login_data(request.form)
            if not isinstance(data, dict):
                data = dict(data)
            email = data.get('email', '').strip().lower()
            password = data.get('password', '')
            user = User.query.filter_by(email=email).first()
            if user:
                from flask import session
                fail_key = f'fail_count_{email}'
                fail_count = session.get(fail_key, 0)
                if user.check_password(password):
                    session[fail_key] = 0
                else:
                    session[fail_key] = fail_count + 1
                    if session[fail_key] >= 3:
                        send_discord_audit_log(
                            action="Login suspeito",
                            user_id=user.id,
                            object_type="Usuário",
                            object_id=user.id,
                            details=f"Múltiplas falhas de login para {email} do IP {request.remote_addr}",
                            ip_address=request.remote_addr
                        )
            if user and user.check_password(password):
                last_ip = getattr(user, 'last_login_ip', None)
                if last_ip and last_ip != request.remote_addr:
                    send_discord_audit_log(
                        action="Login suspeito",
                        user_id=user.id,
                        object_type="Usuário",
                        object_id=user.id,
                        details=f"Login de IP incomum: {request.remote_addr} (último: {last_ip})",
                        ip_address=request.remote_addr
                    )
                user.last_login_ip = request.remote_addr
                db.session.commit()
                print(f"[DEBUG] login_user will be called for user_id={user.id}, email={email}")
                login_user(user)
                print(f"[DEBUG] login_user called, user is_authenticated={user.is_authenticated}")
                audit_auth_attempt(email, True)
                from src.security.audit import log_audit
                print(f"[DEBUG] log_audit will be called for login: user_id={user.id}, email={email}")
                log_audit(
                    action="Login",
                    object_type=user.username,
                    object_id=user.id,
                    details=f"Login bem-sucedido para {email}"
                )
                print(f"[DEBUG] log_audit called for login: user_id={user.id}, email={email}")
                if request.is_json:
                    access_token = create_access_token(identity=user.id)
                    refresh_token = create_refresh_token(identity=user.id)
                    return jsonify({
                        "message": "Login successful",
                        "data": {
                            "email": email,
                            "access_token": access_token,
                            "refresh_token": refresh_token
                        }
                    })
                return redirect(url_for('chat.chat_redirect'))
            audit_auth_attempt(email, False)
            if request.is_json:
                return jsonify({"error": "Invalid credentials", "message": "E-mail ou senha inválidos"}), 401
            flash('E-mail ou senha inválidos')
        except Exception as e:
            audit_auth_attempt(request.form.get('email', ''), False)
            if request.is_json:
                return jsonify({"error": "Validation error", "message": str(e)}), 400
            flash(str(e))
    return render_template('auth/login.html')

@auth.route('/logout')
@login_required
@audit_route
def logout():
    """
    Endpoint de logout.
    ---
    tags:
      - auth
    summary: Faz logout do usuário
    description: Encerra a sessão do usuário atual
    security:
      - bearerAuth: []
    responses:
      200:
        description: Logout bem-sucedido
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Success'
    """
    logout_user()
    if request.is_json:
        return jsonify({"message": "Logout successful"})
    return redirect(url_for('auth.login'))

@auth.route('/refresh', methods=['POST'])
@jwt_required(refresh=True)
@audit_route
def refresh():
    """
    Endpoint de refresh token.
    ---
    tags:
      - auth
    summary: Atualiza o token de acesso
    description: Gera um novo token de acesso usando o refresh token
    security:
      - bearerAuth: []
    responses:
      200:
        description: Token atualizado com sucesso
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Success'
    """
    current_user_id = get_jwt_identity()
    access_token = create_access_token(identity=current_user_id)
    return jsonify({"access_token": access_token})

@auth.route("/login-microsoft")
def login_microsoft():
    try:
        session["state"] = str(uuid.uuid4())
        if request.host.startswith('localhost') or request.host.startswith('127.0.0.1') or os.environ.get('FLASK_ENV') == 'development' or os.environ.get('FLASK_DEBUG') == '1':
            scheme = 'http'
        else:
            scheme = 'https'
        auth_url = _build_msal_app().get_authorization_request_url(
            SCOPE,
            state=session["state"],
            redirect_uri=url_for("auth.authorized_microsoft", _external=True, _scheme=scheme),
            prompt="select_account"
        )
        print('MSAL Auth URL:', auth_url)
        return redirect(auth_url)
    except Exception as e:
        logging.exception('Erro ao iniciar login Microsoft:')
        return jsonify({'error': 'Erro interno do servidor', 'details': str(e)}), 500

@auth.route(REDIRECT_PATH)
def authorized_microsoft():
    try:
        if request.host.startswith('localhost') or request.host.startswith('127.0.0.1') or os.environ.get('FLASK_ENV') == 'development' or os.environ.get('FLASK_DEBUG') == '1':
            scheme = 'http'
        else:
            scheme = 'https'
        if request.args.get('state') != session.get("state"):
            return redirect(url_for("index"))
        if "error" in request.args:
            return f"Erro: {request.args['error']} - {request.args.get('error_description')}"
        if "code" in request.args:
            result = _build_msal_app().acquire_token_by_authorization_code(
                request.args['code'],
                scopes=SCOPE,
                redirect_uri=url_for("auth.authorized_microsoft", _external=True, _scheme=scheme))
            print('MSAL Token Result:', result)
            if "access_token" in result:
                claims = result.get("id_token_claims", {})
                email = claims.get("preferred_username") or claims.get("email")
                username = claims.get("name") or (email.split("@", 1)[0] if email else None)
                if not email:
                    flash("Não foi possível obter o e-mail do usuário Microsoft.")
                    return redirect(url_for("auth.login"))
                user = User.query.filter_by(email=email).first()
                if not user:
                    user = User(username=username, email=email)
                    import secrets
                    user.set_password(secrets.token_urlsafe(16))
                    db.session.add(user)
                    db.session.commit()
                else:
                    if user.username != username and username:
                        user.username = username
                        db.session.commit()
                login_user(user)
                from src.security.audit import log_audit
                print(f"[DEBUG] log_audit will be called for Microsoft login: user_id={user.id}, email={email}")
                log_audit(
                    action="Login",
                    object_type=user.username,
                    object_id=user.id,
                    details=f"Login Microsoft bem-sucedido para {email}"
                )
                print(f"[DEBUG] log_audit called for Microsoft login: user_id={user.id}, email={email}")
                session["user"] = claims
                session["access_token"] = result["access_token"]
                flash("Login Microsoft realizado com sucesso!")
                return redirect(url_for("chat.chat_redirect"))
            else:
                print('Erro ao obter token:', result)
                return f"Erro ao obter token: {result.get('error_description')}"
        return "Login cancelado ou inválido"
    except Exception as e:
        logging.exception('Erro no callback Microsoft:')
        return jsonify({'error': 'Erro interno do servidor', 'details': str(e)}), 500

@auth.route("/logout-microsoft")
def logout_microsoft():
    session.clear()
    return redirect(
        AUTHORITY + "/oauth2/v2.0/logout" +
        f"?post_logout_redirect_uri={url_for('auth.login', _external=True)}"
    ) 