# servidor_app.py
from flask import Flask, render_template, request, redirect, url_for, session, flash, jsonify
import mysql.connector
import random
import os
from datetime import datetime
from collections import defaultdict

app = Flask(__name__)
app.secret_key = os.urandom(24)

# --- CONFIGURAÇÃO DE ACESSOS ---
USERS = {
    "admin": "Admin_Apt_2@25",
    "tic": "Admin_Apt_2@25"
}
SENHA_DO_EXAME = "Teste_Apt_2@25"

# --- CONFIGURAÇÃO DA BASE DE DADOS ---
db_config = {
    'host': '127.0.0.1',
    'user': 'teste_app',
    'password': 'N71j1ngl1c6n',
    'database': 'insutec_acesso'
}

def get_db_connection():
    try:
        conn = mysql.connector.connect(**db_config)
        return conn
    except mysql.connector.Error as err:
        print(f"Erro de Base de Dados: {err}")
        return None

# --- ROTAS DE ACESSO E LOGIN ---
@app.route("/acesso-exame", methods=["GET", "POST"])
def acesso_exame():
    if request.method == "POST":
        senha_inserida = request.form.get('senha')
        if senha_inserida == SENHA_DO_EXAME:
            session['acesso_permitido'] = True
            return redirect(url_for('pagina_inscricao'))
        else:
            flash('Senha de acesso incorreta. Tente novamente.', 'error')
    return render_template("acesso_exame.html")

@app.route("/", methods=["GET"])
def pagina_inscricao():
    if session.get('acesso_permitido'):
        return render_template("inscricao.html")
    else:
        return redirect(url_for('acesso_exame'))

@app.route("/login", methods=["GET", "POST"])
def login():
    error = None
    if request.method == "POST":
        username = request.form.get('username')
        password = request.form.get('password')
        if username in USERS and USERS[username] == password:
            session['logged_in'] = True
            session['username'] = username
            return redirect(url_for('painel_admin'))
        else:
            error = "Utilizador ou palavra-passe incorreta."
    return render_template("login.html", error=error)

@app.route("/logout")
def logout():
    session.clear()
    return redirect(url_for('login'))

# --- ROTAS DO PROCESSO DE TESTE ---
@app.route("/iniciar-teste", methods=["POST"])
def iniciar_teste():
    numero_candidato = request.form['numero_candidato']
    nome_completo = request.form['nome_completo']
    curso_desejado = request.form['curso_desejado']

    conn = get_db_connection()
    if conn is None: return "<h1>Erro: Não foi possível ligar à base de dados.</h1>"
    cursor = conn.cursor(dictionary=True)

    # 1. Verificar se o candidato já existe
    sql_find_candidato = "SELECT id FROM candidatos WHERE numero_candidato = %s"
    cursor.execute(sql_find_candidato, (numero_candidato,))
    candidato_existente = cursor.fetchone()

    candidato_id = None

    if candidato_existente:
        candidato_id = candidato_existente['id']
        
        # 2. BLOQUEIO DE DUPLICAÇÃO TOTAL (Qualquer tentativa anterior barra o acesso)
        sql_check_tentativa = "SELECT id FROM resultados WHERE candidato_id = %s"
        cursor.execute(sql_check_tentativa, (candidato_id,))
        
        if cursor.fetchone():
            cursor.close()
            conn.close()
            return render_template("acesso_negado.html")
    else:
        sql_insert_candidato = "INSERT INTO candidatos (numero_candidato, nome_completo, curso_desejado) VALUES (%s, %s, %s)"
        cursor.execute(sql_insert_candidato, (numero_candidato, nome_completo, curso_desejado))
        conn.commit()
        candidato_id = cursor.lastrowid

    sql_select_teste = "SELECT id FROM testes WHERE curso_associado = %s LIMIT 1"
    cursor.execute(sql_select_teste, (curso_desejado,))
    resultado_teste = cursor.fetchone()

    if not resultado_teste:
        cursor.close()
        conn.close()
        return f"<h1>Erro: Não existem testes disponíveis para o curso de {curso_desejado}.</h1>"

    teste_id = resultado_teste['id']
    cursor.close()
    conn.close()

    return render_template("regras.html", 
                           candidato_id=candidato_id, 
                           teste_id=teste_id, 
                           nome_candidato=nome_completo, 
                           numero_candidato=numero_candidato, 
                           curso_desejado=curso_desejado)

@app.route("/comecar-exame", methods=["POST"])
def comecar_exame():
    candidato_id = request.form['candidato_id']
    teste_id = request.form['teste_id']
    nome_completo = request.form['nome_completo']
    numero_candidato = request.form['numero_candidato']
    curso_desejado = request.form['curso_desejado']

    conn = get_db_connection()
    if conn is None: return "<h1>Erro: Não foi possível ligar à base de dados.</h1>"
    cursor = conn.cursor(dictionary=True)

    perguntas_finais = []

    # --- CONFIGURAÇÃO PARA BOLSEIROS (20 Perguntas) ---
    # Estrutura: 8 Matemática, 8 Lógica, 4 Especialidade
    categorias = {'Matemática': 8, 'Lógica': 8, 'Especialidade': 4}

    categorias_necessarias = list(categorias.keys())
    placeholders = ','.join(['%s'] * len(categorias_necessarias))
    sql_todas_as_perguntas = f"""
        SELECT id, enunciado, opcao_a, opcao_b, opcao_c, opcao_d, categoria
        FROM perguntas
        WHERE teste_id = %s AND categoria IN ({placeholders})
    """
    params = [teste_id] + categorias_necessarias
    cursor.execute(sql_todas_as_perguntas, tuple(params))
    perguntas_disponiveis = cursor.fetchall()

    pool_por_categoria = {cat: [] for cat in categorias_necessarias}
    for p in perguntas_disponiveis:
        pool_por_categoria[p['categoria']].append(p)

    for categoria, limite in categorias.items():
        pool_da_categoria = pool_por_categoria.get(categoria, [])

        if len(pool_da_categoria) < limite:
            cursor.close()
            conn.close()
            return f"<h1>Erro: O banco de dados não tem perguntas suficientes para '{curso_desejado}' na categoria '{categoria}'. Precisa de {limite}, existem {len(pool_da_categoria)}.</h1>"

        perguntas_selecionadas = random.sample(pool_da_categoria, limite)
        perguntas_finais.extend(perguntas_selecionadas)

    random.shuffle(perguntas_finais)
    cursor.close()
    conn.close()

    return render_template("teste.html",
                           candidato_id=candidato_id,
                           teste_id=teste_id,
                           nome_candidato=nome_completo,
                           numero_candidato=numero_candidato,
                           curso=curso_desejado,
                           perguntas=perguntas_finais)

@app.route("/submeter-teste", methods=["POST"])
def submeter_teste():
    candidato_id = request.form['candidato_id']
    teste_id = request.form['teste_id']
    ordem_perguntas_str = request.form.get('ordem_perguntas', '')
    ordem_perguntas = ordem_perguntas_str.split(',')
    tentativa_fraude = request.form.get('tentativa_fraude', '0') == '1'
    
    conn = get_db_connection()
    if conn is None: return "<h1>Erro: Não foi possível ligar à base de dados.</h1>"
    cursor = conn.cursor(dictionary=True)

    ids_perguntas = [key.split('_')[1] for key in request.form if key.startswith('pergunta_')]
    if not ids_perguntas and ordem_perguntas: ids_perguntas = ordem_perguntas
    if not ids_perguntas or ids_perguntas == ['']: return "<h1>Erro: Nenhuma pergunta foi submetida.</h1>"

    sql_perguntas = f"SELECT id, resposta_correta FROM perguntas WHERE id IN ({','.join(['%s'] * len(ids_perguntas))})"
    cursor.execute(sql_perguntas, ids_perguntas)
    perguntas_db_dict = {p['id']: p['resposta_correta'] for p in cursor.fetchall()}

    respostas_corretas = 0
    respostas_para_inserir = []

    for i, pergunta_id_str in enumerate(ordem_perguntas):
        pergunta_id = int(pergunta_id_str)
        resposta_correta = perguntas_db_dict.get(pergunta_id)
        resposta_dada = request.form.get(f'pergunta_{pergunta_id}')
        ordem = i + 1
        foi_correta = (resposta_dada == resposta_correta)
        if foi_correta:
            respostas_corretas += 1
        respostas_para_inserir.append((pergunta_id, resposta_dada if resposta_dada else 'N/A', foi_correta, ordem))

    # Cálculo da nota (para uso interno/admin)
    nota_final = 0.0 if tentativa_fraude else (respostas_corretas / len(ordem_perguntas)) * 20
    status = "Aprovado" if nota_final >= 10 else "Reprovado"

    sql_insert_resultado = "INSERT INTO resultados (candidato_id, teste_id, nota_final, status, tentativa_fraude) VALUES (%s, %s, %s, %s, %s)"
    cursor.execute(sql_insert_resultado, (candidato_id, teste_id, nota_final, status, tentativa_fraude))
    conn.commit()
    resultado_id = cursor.lastrowid

    sql_insert_respostas = "INSERT INTO respostas_candidato (resultado_id, pergunta_id, resposta_dada, foi_correta, ordem) VALUES (%s, %s, %s, %s, %s)"
    dados_respostas = [(resultado_id,) + resp for resp in respostas_para_inserir]
    cursor.executemany(sql_insert_respostas, dados_respostas)
    conn.commit()
    cursor.close()
    conn.close()

    # Redirecionamento condicional
    if tentativa_fraude:
        return render_template("fraude.html")
    else:
        return render_template("conclusao.html")

# --- ROTA DE RESULTADO (Protegida) ---
@app.route("/resultado/<int:resultado_id>")
def pagina_resultado(resultado_id):
    # Bloqueia visualização da nota pelo aluno
    return render_template("conclusao.html")

# --- ADMINISTRAÇÃO ---
@app.route("/admin")
def painel_admin():
    if not session.get('logged_in'):
        return redirect(url_for('login'))
    curso_filtro = request.args.get('curso_filtro', 'todos')
    data_inicio = request.args.get('data_inicio', '')
    data_fim = request.args.get('data_fim', '')
    conn = get_db_connection()
    if conn is None: return "<h1>Erro: Não foi possível ligar à base de dados.</h1>"
    cursor = conn.cursor(dictionary=True)

    sql_base = "SELECT c.id AS candidato_id, r.id AS resultado_id, c.numero_candidato, c.nome_completo, c.curso_desejado, r.nota_final, r.status, r.data_submissao, r.tentativa_fraude FROM resultados r JOIN candidatos c ON r.candidato_id = c.id"
    conditions = []
    params = []
    if curso_filtro != 'todos':
        conditions.append("c.curso_desejado = %s")
        params.append(curso_filtro)
    if data_inicio:
        conditions.append("r.data_submissao >= %s")
        params.append(data_inicio)
    if data_fim:
        conditions.append("r.data_submissao <= %s")
        params.append(data_fim + ' 23:59:59')
    if conditions:
        sql_base += " WHERE " + " AND ".join(conditions)
    sql_base += " ORDER BY r.data_submissao DESC;"
    cursor.execute(sql_base, tuple(params))
    resultados = cursor.fetchall()
    cursor.close()
    conn.close()

    return render_template("admin.html",
                           resultados=resultados,
                           curso_selecionado=curso_filtro,
                           data_inicio=data_inicio,
                           data_fim=data_fim,
                           now=datetime.now())

@app.route("/apagar_candidato/<int:candidato_id>", methods=["POST"])
def apagar_candidato(candidato_id):
    if not session.get('logged_in'):
        return redirect(url_for('login'))

    conn = get_db_connection()
    if conn is None:
        flash("Erro de base de dados.", "error")
        return redirect(url_for('painel_admin'))

    cursor = conn.cursor(dictionary=True)

    try:
        cursor.execute("SELECT numero_candidato, nome_completo, curso_desejado FROM candidatos WHERE id = %s", (candidato_id,))
        candidato_info = cursor.fetchone()

        if candidato_info:
            user_que_apagou = session.get('username', 'desconhecido')
            detalhes = f"Candidato: {candidato_info['nome_completo']} ({candidato_info['numero_candidato']}) - Curso: {candidato_info['curso_desejado']}"

            sql_log = "INSERT INTO historico_acoes (utilizador, acao, detalhes, data_acao) VALUES (%s, %s, %s, NOW())"
            log_cursor = conn.cursor()
            log_cursor.execute(sql_log, (user_que_apagou, 'apagar_candidato', detalhes))
            log_cursor.close()

        sql_delete = "DELETE FROM candidatos WHERE id = %s"
        delete_cursor = conn.cursor()
        delete_cursor.execute(sql_delete, (candidato_id,))
        delete_cursor.close()

        conn.commit()
        flash("Candidato apagado com sucesso e ação registada no histórico.", "success")

    except mysql.connector.Error as err:
        print(f"Erro ao apagar o candidato: {err}")
        flash("Ocorreu um erro ao apagar o candidato.", "error")
    finally:
        cursor.close()
        conn.close()

    return redirect(url_for('painel_admin'))

@app.route("/admin/ver_prova/<int:resultado_id>")
def ver_prova(resultado_id):
    if not session.get('logged_in'):
        return redirect(url_for('login'))
    conn = get_db_connection()
    if conn is None: return "<h1>Erro: Não foi possível ligar à base de dados.</h1>"
    cursor = conn.cursor(dictionary=True)

    sql_resultado = "SELECT r.*, c.nome_completo, c.numero_candidato, c.curso_desejado FROM resultados r JOIN candidatos c ON r.candidato_id = c.id WHERE r.id = %s"
    cursor.execute(sql_resultado, (resultado_id,))
    resultado = cursor.fetchone()
    if not resultado:
        return "<h1>Erro: Resultado não encontrado.</h1>"

    sql_prova = """
        SELECT p.enunciado, p.opcao_a, p.opcao_b, p.opcao_c, p.opcao_d,
               rc.resposta_dada, p.resposta_correta, rc.foi_correta
        FROM respostas_candidato rc
        JOIN perguntas p ON rc.pergunta_id = p.id
        WHERE rc.resultado_id = %s
        ORDER BY rc.ordem ASC;
    """
    cursor.execute(sql_prova, (resultado_id,))
    prova = cursor.fetchall()

    cursor.close()
    conn.close()

    return render_template("ver_prova.html", resultado=resultado, candidato=resultado, prova=prova)

# --- ROTAS DE ESTATÍSTICAS (RESTABELECIDAS) ---

@app.route("/admin/estatisticas")
def estatisticas_page():
    if not session.get('logged_in'):
        return redirect(url_for('login'))
    return render_template("estatisticas.html")

@app.route("/admin/estatisticas_data")
def estatisticas_data():
    if not session.get('logged_in'):
        return jsonify({"error": "Não autorizado"}), 401

    conn = get_db_connection()
    if conn is None:
        return jsonify({"error": "Erro de base de dados"}), 500
    cursor = conn.cursor(dictionary=True)

    query_quantitativo = """
        SELECT
            c.curso_desejado,
            COUNT(r.id) AS total_testes,
            SUM(CASE WHEN r.status = 'Aprovado' THEN 1 ELSE 0 END) AS total_aprovados,
            SUM(r.tentativa_fraude) AS total_fraudes
        FROM resultados r
        JOIN candidatos c ON r.candidato_id = c.id
        GROUP BY c.curso_desejado
        ORDER BY c.curso_desejado;
    """
    cursor.execute(query_quantitativo)
    dados_quantitativos = cursor.fetchall()

    total_geral = {
        "curso_desejado": "TOTAL GERAL",
        "total_testes": sum(d['total_testes'] for d in dados_quantitativos),
        "total_aprovados": sum(d['total_aprovados'] for d in dados_quantitativos),
        "total_fraudes": sum(d['total_fraudes'] for d in dados_quantitativos)
    }
    dados_quantitativos.append(total_geral)

    query_categoria = """
        SELECT
            c.curso_desejado,
            p.categoria,
            SUM(rc.foi_correta) AS acertos,
            COUNT(rc.id) AS total_respostas
        FROM respostas_candidato rc
        JOIN perguntas p ON rc.pergunta_id = p.id
        JOIN resultados r ON rc.resultado_id = r.id
        JOIN candidatos c ON r.candidato_id = c.id
        GROUP BY c.curso_desejado, p.categoria
        ORDER BY c.curso_desejado, p.categoria;
    """
    cursor.execute(query_categoria)
    desempenho_db = cursor.fetchall()

    desempenho_por_curso = defaultdict(lambda: {'labels': [], 'data': []})
    for row in desempenho_db:
        curso = row['curso_desejado']
        taxa_acerto = (row['acertos'] / row['total_respostas']) * 100 if row['total_respostas'] > 0 else 0
        desempenho_por_curso[curso]['labels'].append(row['categoria'])
        desempenho_por_curso[curso]['data'].append(round(taxa_acerto, 2))

    query_perguntas = """
        SELECT
            p.enunciado,
            SUM(rc.foi_correta) AS acertos,
            COUNT(rc.id) AS total,
            (SUM(rc.foi_correta) / COUNT(rc.id)) * 100 AS taxa_acerto
        FROM respostas_candidato rc
        JOIN perguntas p ON rc.pergunta_id = p.id
        GROUP BY p.id, p.enunciado
        HAVING COUNT(rc.id) > 4
    """
    cursor.execute(f"{query_perguntas} ORDER BY taxa_acerto ASC LIMIT 5;")
    perguntas_dificeis = cursor.fetchall()

    cursor.execute(f"{query_perguntas} ORDER BY taxa_acerto DESC LIMIT 5;")
    perguntas_faceis = cursor.fetchall()

    query_histograma = """
        SELECT
            SUM(CASE WHEN nota_final >= 0 AND nota_final < 5 THEN 1 ELSE 0 END) AS '0-4',
            SUM(CASE WHEN nota_final >= 5 AND nota_final < 10 THEN 1 ELSE 0 END) AS '5-9',
            SUM(CASE WHEN nota_final >= 10 AND nota_final < 15 THEN 1 ELSE 0 END) AS '10-14',
            SUM(CASE WHEN nota_final >= 15 AND nota_final <= 20 THEN 1 ELSE 0 END) AS '15-20'
        FROM resultados;
    """
    cursor.execute(query_histograma)
    dist_notas_db = cursor.fetchone()
    distribuicao_notas = {
        "labels": list(dist_notas_db.keys()),
        "data": [int(v) if v is not None else 0 for v in dist_notas_db.values()]
    }

    query_evolucao = """
        SELECT
            DATE(data_submissao) as dia,
            COUNT(id) as total_testes,
            SUM(CASE WHEN status = 'Aprovado' THEN 1 ELSE 0 END) as total_aprovados
        FROM resultados
        GROUP BY DATE(data_submissao)
        ORDER BY dia;
    """
    cursor.execute(query_evolucao)
    evolucao_db = cursor.fetchall()
    evolucao_tempo = {
        "labels": [row['dia'].strftime('%d/%m') for row in evolucao_db],
        "testesData": [row['total_testes'] for row in evolucao_db],
        "aprovadosData": [row['total_aprovados'] for row in evolucao_db]
    }

    cursor.close()
    conn.close()

    data_para_graficos = {
        "dadosQuantitativos": dados_quantitativos,
        "desempenhoPorCurso": desempenho_por_curso,
        "perguntasDificeis": perguntas_dificeis,
        "perguntasFaceis": perguntas_faceis,
        "distribuicaoNotas": distribuicao_notas,
        "evolucaoTempo": evolucao_tempo
    }

    return jsonify(data_para_graficos)

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=5000, debug=True)
