# app.py (Aplicativo Flask principal)

import os
from flask import Flask, render_template, request, redirect, url_for, flash
import pandas as pd
from datetime import datetime
import matplotlib.pyplot as plt
from typing import List, Dict, Any, Optional
from Estrutura import TEstrutura, TOption, TOptionStatus, TCategoria
import math
import json



def F_num(valor, colorir=False, percentual=False):
    """
    A função definitiva de formatação.
    - Se erro (NaN/Inf/None): retorna --- em preto.
    - Se colorir=True: verde se > 0, vermelho se < 0.
    - Se percentual=True: adiciona % no final.
    """
    if valor is None or not math.isfinite(valor):
        return '<span>---</span>'

    # Verifica se o valor é um número inteiro (ex: 400 ou 400.0)
    if valor == int(valor):
        texto = f"{int(valor):,}" # Formata com separador de milhar, sem decimais
    else:
        texto = f"{valor:,.2f}" # Formata com milhar e 2 casas decimais

    if percentual:
        texto += "%"

    if colorir:
        if valor > 0.001:
            return f'<span class="text-profit">{texto}</span>'
        elif valor < -0.001:
            return f'<span class="text-loss">{texto}</span>'

    return f'<span>{texto}</span>'



# Importa as funções de utilidade e persistência do server.py
# Inclui também as constantes globais usadas pelas funções
from Server import (
    carregar_estrategias_salvas_lista,
    carregar_estrutura_detalhada,
    sincronizar_propriedades_estrutura,
    carregar_estruturas_em_lote,
    cotar_e_atualizar_estruturas_em_lote,
    excluir_estrategia_por_id,
    obter_resumo_estrutura_organizado,
    obter_opcoes_estrutura,
    gerar_svg_payoff_real,
    obter_estrutura_para_edicao,
    salvar_estrutura,
    ESTRUTURAS_DIR,
    ESTRUTURAS_CARREGADAS,
    F,
)

##def ToFloat(num):
##    return

# ====================================================================
# CONFIGURAÇÕES DO FLASK
# ====================================================================

app = Flask(__name__, template_folder='templates')
app.secret_key = 'uma_chave_secreta_muito_longa_e_unica_para_producao'

@app.context_processor
def utility_processor():
    return dict(F=F)

# Garantir a criação do diretório (embora o server.py já faça)
if not os.path.exists(ESTRUTURAS_DIR):
    os.makedirs(ESTRUTURAS_DIR)


# |====================================================================================|
# |====================================================================================|
# ROTAS DO FLASK
# |====================================================================================|
# |====================================================================================|


# No arquivo App.py na rota @app.route('/')
# |====================================================================================|
# |---   app.py   ----    Index    ----                                           -----|
# |====================================================================================|
# App: Index
@app.route('/', methods=['GET'])
def index():
    """Rota principal: Dashboard de Análise de Estruturas."""
    global ESTRUTURAS_CARREGADAS

    # 1. Carrega e Cota tudo
    carregar_estruturas_em_lote()
    cotar_e_atualizar_estruturas_em_lote()

    # 2. Prepara a lista para o menu lateral e para a grid
    estrategias_exibidas = []

    for est in ESTRUTURAS_CARREGADAS.values():
        # Chama a NOVA função única
        # Sincroniza os valores (Usando a função única que definimos)
        sincronizar_propriedades_estrutura(est)
        qb = abs(est.QuantBase) if est.QuantBase != 0 else 1

        # Cálculo de porcentagem seguro
        #entrada_total = est.ValorIn
        lucro_perc = (est.ValorLucro / est.ValorIn * 100) if est.ValorIn != 0 else float('nan')

        lucro_normalizado = (est.ValorLucro / est.LucroAim * 1) if est.ValorIn != 0 else -1
        print('\n\nest.ValorLucro / est.LucroAim',est.ValorLucro , est.LucroAim)
        #dias_normalizado =

        estrategias_exibidas.append({
            "id": est.id,
            "Estratégia": est.Nome,
            "Ativo": est.AtivoBase,
            "Dias":     {"u": est.DTE, "d": est.MenorVencimento},
            "ValorIn":  {"u": F(est.ValorIn / qb), "d": F(est.ValorIn)},
            "ValorNow": {"u": F(est.ValorNow / qb), "d": F(est.ValorNow)},
            "Alvo":     {"u": F(est.ValorAim / qb), "d": F(est.ValorAim )},
            # ADICIONE ESTA LINHA QUE ESTAVA FALTANDO:
            "ValorOut": {"u": F(est.ValorOut / qb), "d": F(est.ValorOut)},
            "PL":       {"u": F(est.ValorLucro / qb, colorir=True), "d": F(est.ValorLucro, colorir=True)},
            "PLPerc":   {"u": F(lucro_perc, colorir=True, percentual=True), "d": F(lucro_normalizado, colorir=True, percentual=True)},
            "Progresso":{"u": f"{F(est.ValorLucro / qb)}/{F(est.LucroAim / qb )}",
                         "d": f"{F(est.MenorVencimento)}/{F(est.DTE)}",}
        })

    # 3. Gerenciamento da estratégia selecionada (Detalhes)
    estrategia_id_param = request.args.get('id', estrategias_exibidas[0]['id'] if estrategias_exibidas else None)
    estrutura_para_detalhes = ESTRUTURAS_CARREGADAS.get(estrategia_id_param)

    if estrutura_para_detalhes:
        # Sincroniza usando a ÚNICA função de verdade (definida no passo anterior)
        # Isso garante que ValorIn é Unitário e ValorNow é Total
        sincronizar_propriedades_estrutura(estrutura_para_detalhes)

        # Gera o resumo organizado (Tabela inferior esquerda)
        resumo_data = obter_resumo_estrutura_organizado(estrutura_para_detalhes)

        # Gera as pernas (Tabela inferior direita)
##        df_opcoes = obter_opcoes_estrutura_df(estrutura_para_detalhes)
##        pernas_data = df_opcoes.to_dict(orient='records')
        pernas_data = obter_opcoes_estrutura(estrutura_para_detalhes)

        # Gera o gráfico (Payoff)
        grafico_payoff = gerar_svg_payoff_real(estrutura_para_detalhes)
    else:
        resumo_data = obter_resumo_estrutura_organizado(None)
        pernas_data = [] # Lista vazia em vez de string HTML
        grafico_payoff = ""


    # 4. Construção da Grid Superior (Apenas se houver dados)
##    if estrategias_exibidas:
##        df_estrategias = pd.DataFrame(estrategias_exibidas)
##
##        # Coluna de ações
##        df_estrategias['Ações'] = df_estrategias.apply(
##            lambda row: (
##                f'<a href="{url_for("index", id=row["id"])}" class="btn btn-sm btn-primary">Selecionar</a> '
##                f'<a href="{url_for("excluir_estrategia", id=row["id"])}" class="btn btn-sm btn-danger">Excluir</a>'
##            ), axis=1
##        )
##
##        # Formatação para exibição na Tabela HTML (Unitário e Total entre parênteses)
##        for col in ['ValorIn', 'ValorNow', 'Alvo', 'ValorOut', 'PL']:
##            if col in df_estrategias.columns:
##                df_estrategias[col] = df_estrategias[col].apply(lambda x: f"{x['u']:,.2f} ({x['t']:,.2f})")

##        colunas = ['Estratégia', 'Ativo', 'Dias', 'ValorIn', 'Alvo', 'ValorNow', 'PL', 'Ações', 'PLPerc']
##        tabela_estrategias_html = df_estrategias[colunas].to_html(
##            classes='table table-striped table-hover text-center align-middle',
##            index=False, escape=False
##        )
##    else:
##        # Caso a pasta de estruturas esteja vazia
##        tabela_estrategias_html = "<p class='alert alert-info'>Nenhuma estratégia encontrada.</p>"

    return render_template(
        'index.html',
        resumo_data = resumo_data,
        pernas_data = pernas_data,
        grafico_payoff = grafico_payoff,
        estrategias_menu = estrategias_exibidas, # Lista com .u e .t
      #  tabela_estrategias = tabela_estrategias_html # Grid formatada
    )



# |====================================================================================|
# |---   app.py   ----    Importar    ----     App: Importar                      -----|
# |====================================================================================|
# App: Importar
@app.route('/importar', methods=['GET', 'POST'])
def importar_estrutura():
    """Rota: Trata a importação da estrutura a partir da Área de Transferência."""
    if request.method == 'POST':
        nome_estrategia = request.form['nome_estrategia']
        quant_base = int(request.form.get('quant_base', 1) or 1)
        dte = int(request.form.get('dte', 21))
        ativo_base = request.form.get('ativo_base', '').upper()
        # Limpa a vírgula para ponto antes de converter para float
        valor_ativo_in_str = request.form.get('valor_ativo_in', '0').replace(',', '.')
        valor_ativo_in = float(valor_ativo_in_str) if valor_ativo_in_str else 0.1
        clipboard_data = request.form['clipboard_data']

        if not clipboard_data or not nome_estrategia:
            flash("Preencha todos os campos!", 'danger')
            return redirect(url_for('importar_estrutura'))

        try:
            # 1. Importar estrutura (Lógica em Estrutura.py)
            estrutura: TEstrutura = TEstrutura.ImportarEstruturaDeStringTransacoes(clipboard_data)

            # Aplicando os valores manuais informados no form
            estrutura.Nome = nome_estrategia
            estrutura.QuantBase = quant_base
            estrutura.DTE = dte
            estrutura.MenorVencimento = dte # Inicializa com o informado
            estrutura.AtivoBase = ativo_base
            estrutura.ValorAtivoIn = valor_ativo_in

            if not estrutura.Options:
                 flash("Nenhuma perna válida encontrada na importação. Verifique a formatação.", 'danger')
                 return redirect(url_for('importar_estrutura'))

            # 2. Enriquecimento dos dados de mercado (Lógica em Estrutura.py/Oplab.py)
            estrutura.CotarEstruturaAPI()

            estrutura.RestoreValorIn()

            estrutura.CalcValorIn() # Garante que ValorIn total está atualizado após cotação

            # 3. Salva a estrutura (Usando função do server.py)
            id_salvo = salvar_estrutura(estrutura, nome_estrategia)
            #print('\n\n Rota: importar - salvar_estrutura: \n', vars(estrutura),'\n\n')

            flash(f"Estratégia '{nome_estrategia}' salva e cotada com sucesso!", 'success')
            return redirect(url_for('index', id=id_salvo))

        except Exception as e:
            flash(f"Erro ao processar a estrutura: {str(e)}", 'danger')
            print(f"Erro no POST de importação: {e}")
            return redirect(url_for('importar_estrutura'))

    # Método GET: Exibe o formulário de importação
    return render_template('importar.html')



# |====================================================================================|
# |---   app.py   ----    Excluir    ----                                         -----|
# |====================================================================================|
# App: Excluir
@app.route('/excluir/<string:id>')
def excluir_estrategia(id):
    """Rota para exclusão de uma estrutura salva."""

    sucesso, mensagem = excluir_estrategia_por_id(id) # <--- Usando função do server.py

    if sucesso:
        flash(f"Estratégia '{mensagem}' excluída com sucesso!", 'success')
    else:
        # Se não for sucesso, 'mensagem' já contém o erro ou "Estratégia não encontrada."
        flash(mensagem, 'danger')

    return redirect(url_for('index'))



# |====================================================================================|
# |---   app.py   ----    Editar    ----    (Trecho da rota editar_estrategia)    -----|
# |====================================================================================|
# App: Editar
@app.route('/editar/<string:id>', methods=['GET', 'POST'])
def editar_estrategia(id):
    """
    Rota para carregar, editar e salvar alterações em uma TEstrutura.
    """
    estrutura = ESTRUTURAS_CARREGADAS.get(id)

    if not estrutura:
        flash(f"Estrutura com ID {id} não encontrada ou não carregada.", 'danger')
        return redirect(url_for('index'))

    if request.method == 'POST':
        try:
            # 1. Atualizar Propriedades da Estrutura (Todos os campos)
            print('\n\nRota editar: ',request.form)

            # Bloco Montagem
            estrutura.Nome = request.form.get('nome_estrategia')
            try:
                estrutura.Categoria = TCategoria(request.form.get('categoria', ''))
            except ValueError:
                estrutura.Categoria = TCategoria.SEM_CATEGORIA
            estrutura.Corretora = request.form.get('corretora', '')
            estrutura.QuantBase = int(request.form.get('quant_base', 1))
            estrutura.DTE = int(request.form.get('dte', 0))
            estrutura.AtivoBase = request.form.get('ativo_base').upper()
            estrutura.ValorAtivoIn = float(request.form.get('valor_ativo_in', 0).replace(',', '.'))

            # Bloco Acompanhamento
            estrutura.Info = request.form.get('info', '')

            # Bloco Desmontagem
            estrutura.ValorAim = float(request.form.get('valor_aim', 0).replace(',', '.'))
            estrutura.ValorOut = float(request.form.get('valor_out', 0).replace(',', '.'))

            # Captura o DTE do formulário
            dte_form = request.form.get('dte')
            if dte_form:
                estrutura.DTE = int(dte_form)
                # Sincroniza o MenorVencimento se for uma estrutura de vencimento único
                estrutura.MenorVencimento = estrutura.DTE

            # Captura o Info (garante que não venha None)
            estrutura.Info = request.form.get('info', '')

            # 2. Atualizar as Pernas
            num_opcoes = int(request.form.get('num_opcoes', 0))
            estrutura.Options.Clear()

            for i in range(num_opcoes):
                nome = request.form.get(f'nome_{i}', '').upper().strip()
                if not nome:
                    continue

                oper = request.form.get(f'oper_{i}', 'C')

                # Pegamos os valores brutos do form
                quant_raw = request.form.get(f'quant_{i}', '0')
                valorin_raw = request.form.get(f'valorin_{i}', '0').replace(',', '.')

                # Conversões seguras para números
                quant_abs = abs(int(quant_raw))
                # Correção do nome da variável aqui:
                valor_transacao = float(valorin_raw) if valorin_raw else 0.0
                status_raw = request.form.get(f'status_{i}', 'Ativa')

                # Cria o objeto da perna
                op = TOption()
                op.Nome = nome
                op.Quant = quant_abs if oper == 'C' else -quant_abs
                op.Oper = oper
                op.ValorIn = valor_transacao
                op.TransacaoValue = valor_transacao
                op.Status = TOptionStatus(status_raw)

                # Decodificação inicial do tipo (Call/Put/Ativo)
                tipo, _, _, _ = TEstrutura.DecodificarPapelOpcao(nome)
                op.Tipo = tipo

                estrutura.Options.Add(op)

            # 3. Sincroniza dados técnicos (Strike/Vencimento) via Web
            # Isso preenche os campos que deixamos como "read-only" no HTML
            estrutura.CotarEstruturaAPI()

            # Restaura o ValorIn a partir do TransacaoValue (protege contra lógicas de simulação)
            estrutura.RestoreValorIn()

            # 4. Finalização e Salvamento
            estrutura.CalcValorIn()
            salvar_estrutura(estrutura, estrutura.Nome)

            flash(f"Estrutura '{estrutura.Nome}' atualizada com sucesso!", 'success')
            return redirect(url_for('index', id=estrutura.id))

        except Exception as e:
            flash(f"Erro ao salvar alterações: {e}", 'danger')
            opcoes_dados = obter_estrutura_para_edicao(estrutura)
            return render_template('editar.html', estrutura=estrutura, opcoes_dados=opcoes_dados)

    # Lógica GET: Exibir o Formulário
    opcoes_dados = obter_estrutura_para_edicao(estrutura)
    #print('\n\n Rota: editar - render_template: \n', vars(estrutura),'\n\n')
    return render_template(
        'editar.html',
        estrutura=estrutura,
        opcoes_dados=opcoes_dados
    )

# --- app.py (Corrigir rota index para chamar a nova) ---
# O import do 'obter_estrutura_para_edicao' precisa ser adicionado no app.py

@app.route('/relatorio', methods=['GET'])
def relatorio():
    global ESTRUTURAS_CARREGADAS

    carregar_estruturas_em_lote()
    cotar_e_atualizar_estruturas_em_lote()

    relatorio_dados = []

    for est in ESTRUTURAS_CARREGADAS.values():
        sincronizar_propriedades_estrutura(est)
        qb = abs(est.QuantBase) if est.QuantBase != 0 else 1

        entrada_total = est.ValorIn
        lucro_perc = (est.ValorLucro / entrada_total * 100) if entrada_total != 0 else float('nan')
        #print('\n\nest.ValorLucro',est.ValorLucro,'entrada_total', entrada_total)
        lucro_normalizado = (est.ValorLucro / est.LucroAim * 1) if est.LucroAim != 0 else float('nan')

        resumo = {
            "id": est.id,
            "Estratégia": est.Nome,
            "Ativo": est.AtivoBase,
            "Dias":     {"u": est.DTE, "d": est.MenorVencimento},
            "ValorIn":  {"u": F(est.ValorIn / qb), "d": F(est.ValorIn)},
            "ValorNow": {"u": F(est.ValorNow / qb), "d": F(est.ValorNow)},
            "Alvo":     {"u": F(est.ValorAim / qb), "d": F(est.ValorAim)},
            "PL":       {"u": F(est.ValorLucro / qb, colorir=True), "d": F(est.ValorLucro, colorir=True)},
            "PLPerc":   {"u": F(lucro_perc, colorir=True, percentual=True)},
            "Progresso":{"u": f"{F(est.ValorLucro / qb)}/{F(est.LucroAim / qb)}",
                         "d": f"{F(est.MenorVencimento)}/{F(est.DTE)}"},
        }

        resumo_data = obter_resumo_estrutura_organizado(est)
        pernas_data = obter_opcoes_estrutura(est)
        grafico_payoff = gerar_svg_payoff_real(est)

        relatorio_dados.append({
            "resumo": resumo,
            "resumo_data": resumo_data,
            "pernas_data": pernas_data,
            "grafico_payoff": grafico_payoff,
            "raw": est,
        })


    # Agrupamentos para o sumário
    from collections import defaultdict

    grupos_corretora = defaultdict(list)
    grupos_categoria = defaultdict(list)
    totais_geral = {"total_in": 0.0, "total_pl": 0.0}

    for item in relatorio_dados:
        est = item["raw"]
        qb = abs(est.QuantBase) if est.QuantBase else 1
        vi_tot = est.ValorIn * qb
        pl_val = est.ValorLucro
        pl_perc = (pl_val / vi_tot * 100) if vi_tot != 0 else 0.0

        linha = {
            "nome": est.Nome,
            "corretora": est.Corretora or "Sem Corretora",
            "categoria": est.Categoria.value if hasattr(est.Categoria, 'value') else (est.Categoria or "Sem Categoria"),
            "dias": est.MenorVencimento,
            "vi_unit": est.ValorIn / qb,
            "vi_tot": vi_tot / qb,
            "pl_val": pl_val /qb,
            "pl_perc": pl_perc,
            "id": est.id,
        }

        grupos_corretora[linha["corretora"]].append(linha)
        grupos_categoria[linha["categoria"]].append(linha)
        totais_geral["total_in"] += vi_tot
        totais_geral["total_pl"] += pl_val

    # Subtotais por corretora
    sumario_corretoras = []
    for cor, linhas in grupos_corretora.items():
        tot_in = sum(l["vi_tot"] for l in linhas)
        tot_pl = sum(l["pl_val"] for l in linhas)
        sumario_corretoras.append({
            "nome": cor,
            "linhas": linhas,
            "tot_in": tot_in,
            "tot_pl": tot_pl,
            "tot_perc": (tot_pl / tot_in * 100) if tot_in != 0 else 0.0,
        })

    # Subtotais por categoria
    sumario_categorias = []
    for cat, linhas in grupos_categoria.items():
        tot_in = sum(l["vi_tot"] for l in linhas)
        tot_pl = sum(l["pl_val"] for l in linhas)
        sumario_categorias.append({
            "nome": cat,
            "tot_in": tot_in,
            "tot_pl": tot_pl,
            "tot_perc": (tot_pl / tot_in * 100) if tot_in != 0 else 0.0,
        })

    totais_geral["tot_perc"] = (
        totais_geral["total_pl"] / totais_geral["total_in"] * 100
        if totais_geral["total_in"] != 0 else 0.0
    )

    return render_template(
        'relatorio_sumario_a.html',
        relatorio_dados=relatorio_dados,
        sumario_corretoras=sumario_corretoras,
        sumario_categorias=sumario_categorias,
        totais_geral=totais_geral,
        now=datetime.now().strftime('%d/%m/%Y %H:%M')
    )



# ====================================================================
# 4. EXECUÇÃO
# ====================================================================

if __name__ == '__main__':
    # Garante que o Matplotlib não tente abrir janelas (útil em alguns ambientes de servidor)
    plt.switch_backend('Agg')
    app.run(host='0.0.0.0', port=8004, debug=True, use_reloader=False)
# App: =====================================