#!/usr/bin/env python3
# =================================================================================================
# SERVIDOR DE API LOCAL PARA EXCEL (v2 - Investidor10)
# =================================================================================================
#
# DESCRIÇÃO:
# Este script Python inicia um servidor web local na porta 5000. Ele atua como uma API 
# personalizada para buscar dados financeiros de fontes externas e fornecê-los de forma 
# formatada para uso em planilhas do Excel, principalmente através de funções VBA.
#
# FONTES DE DADOS:
# 1. Dados de Opções: https://opcoes.net.br/
# 2. Dados de Dividendos: https://investidor10.com.br/
#
# DEPENDÊNCIAS:
# Este script requer as bibliotecas 'requests' e 'beautifulsoup4' para o scraping de dividendos.
# Instale-as no ambiente virtual do projeto com:
# pip install requests beautifulsoup4
#
# COMO EXECUTAR (COMO SERVIÇO SYSTEMD):
# 1. Coloque este arquivo em /opt/excel-server/excel-server.py.
# 2. Crie um ambiente virtual em /opt/excel-server/venv e instale as dependências.
# 3. Crie um arquivo .service em /etc/systemd/system/ e configure-o para executar este script.
# 4. Gerencie o serviço com 'sudo systemctl start/stop/status excel-server'.
#
# -------------------------------------------------------------------------------------------------
# DOCUMENTAÇÃO DA API (COMO USAR NO EXCEL)
# -------------------------------------------------------------------------------------------------
#
# Endpoint Principal: http://SEU_IP:5000/dados
#
# Parâmetros Obrigatórios:
#   - code: O ticker do ativo ou código da opção (ex: petr4, BOVAJ28).
#   - field: A informação desejada.
#
# --- FUNCIONALIDADES DISPONÍVEIS ---
#
# 1. DADOS DE OPÇÕES
#    - Exemplo de uso no Excel (VBA): GetStrikeEVencimento("BOVAJ28")
#    - Parâmetro 'field' para ambos: "strike-vencimento" (ou "vencimento-strike")
#      - URL: http://SEU_IP:5000/dados?code=BOVAJ28&field=strike-vencimento
#      - Retorna: Uma string com os valores separados por pipe '|' (ex: "146,00|17/10/2025").
#
# 2. DADOS DE DIVIDENDOS
#    - Exemplo de uso no Excel (VBA): GetDividendos("petr4")
#    - Parâmetro 'field': "dividendos"
#      - URL: http://SEU_IP:5000/dados?code=petr4&field=dividendos
#      - Retorna: Uma única string com a tabela de proventos.
#        - As colunas são separadas por ponto e vírgula (';').
#        - As linhas são separadas por pipe ('|').
#        - Otimizado para a função TEXTSPLIT do Excel ou para ser processado em VBA.
#
# =================================================================================================

# --- INÍCIO DO CÓDIGO DO SERVIDOR ---

# Módulos nativos
from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import urlparse, parse_qs
from urllib.request import urlopen, Request
import json
import re
import socket

# Módulos externos (requer instalação)
import requests
from bs4 import BeautifulSoup

# ==============================================================================
# FUNÇÃO 1: SCRAPER DE OPÇÕES (opcoes.net.br)
# ==============================================================================
def parse_dados_da_opcao(codigo_opcao: str) -> dict:
    url = f"https://opcoes.net.br/{codigo_opcao.upper()}"
    print(f"[Opções] Buscando dados em: {url}")
    try:
        req = Request(url, headers={'User-Agent': 'Mozilla/5.0'})
        with urlopen(req) as response:
            html_str = response.read().decode('utf-8')
        match = re.search(r'<title>(.*?)</title>', html_str, re.IGNORECASE)
        if not match: return {"erro": "Tag <title> não encontrada."}
        title_text = match.group(1)
        partes = title_text.split(' - ')
        if len(partes) >= 3:
            return {
                "strike": partes[1].replace('Strike R$', '').strip(),
                "vencimento": partes[2].replace('Vencimento ', '').strip()
            }
        else:
            return {"erro": f"Formato do título inesperado: '{title_text}'"}
    except Exception as e:
        return {"erro": f"Falha ao buscar dados da opção: {str(e)}"}

# ==============================================================================
# FUNÇÃO 2: SCRAPER DE DIVIDENDOS (investidor10.com.br)
# ==============================================================================
def scrape_proventos_investidor10(ticker: str) -> list:
    """
    Faz o scraping da tabela de proventos do site Investidor10 e retorna
    uma lista de dicionários com os 10 proventos mais recentes.
    """
    url = f"https://investidor10.com.br/acoes/{ticker.lower()}/"
    print(f"[Dividendos] Buscando dados na fonte: {url}")
    
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36'
    }

    try:
        response = requests.get(url, headers=headers)
        response.raise_for_status()
        soup = BeautifulSoup(response.content, 'html.parser')
        
        proventos_table = soup.find('table', id='table-dividends-history')
        if not proventos_table:
            print("Erro: Tabela com id 'table-dividends-history' não foi encontrada.")
            return []
            
        proventos_list = []
        for row in proventos_table.find('tbody').find_all('tr'):
            cols = row.find_all('td')
            if len(cols) >= 4:
                proventos_list.append({
                    'Tipo': cols[0].text.strip(),
                    'Data Com': cols[1].text.strip(),
                    'Valor': cols[3].text.strip() # Mantém a vírgula original do site
                })
        
        return proventos_list[:10]

    except Exception as e:
        print(f"Erro no scraper de dividendos (Investidor10): {e}")
        return []

def formatar_proventos_para_excel(lista_proventos: list) -> str:
    """Formata a lista de proventos para ser usada com TEXTSPLIT no Excel."""
    if not lista_proventos: return ""
    col_sep = ';'
    row_sep = '|'
    linhas_formatadas = []
    for provento in lista_proventos:
        campos = ["", "", "Venda", provento['Tipo'], provento['Data Com'], "", provento['Valor']]
        linhas_formatadas.append(col_sep.join(campos))
    return row_sep.join(linhas_formatadas)

# ==============================================================================
# SERVIDOR HTTP
# ==============================================================================
class APIServer(BaseHTTPRequestHandler):
    def do_GET(self):
        parsed_url = urlparse(self.path)
        if parsed_url.path == '/dados':
            params = parse_qs(parsed_url.query)
            codigo = params.get('code', [None])[0]
            field = params.get('field', [None])[0]

            if not codigo or not field:
                self._send_json_response(400, {"erro": "Os parâmetros 'code' e 'field' são obrigatórios."})
                return
            
            if field == 'dividendos':
                dados_proventos = scrape_proventos_investidor10(codigo)
                string_formatada = formatar_proventos_para_excel(dados_proventos)
                self._send_text_response(200, string_formatada)
            else:
                dados_opcoes = parse_dados_da_opcao(codigo)
                if "erro" in dados_opcoes:
                    self._send_json_response(500, dados_opcoes)
                    return
                
                campos_lista = field.split('-')
                valores_resposta = []
                for campo in campos_lista:
                    if campo in dados_opcoes:
                        valores_resposta.append(dados_opcoes[campo])
                    else:
                        self._send_json_response(400, {"erro": f"O campo de opção '{campo}' é inválido."})
                        return
                
                self._send_text_response(200, "|".join(valores_resposta))
        else:
            self._send_json_response(404, {"erro": "Endpoint não encontrado. Use /dados"})

    def _send_json_response(self, status_code, data):
        self.send_response(status_code)
        self.send_header('Content-type', 'application/json')
        self.end_headers()
        self.wfile.write(json.dumps(data).encode('utf-8'))
        
    def _send_text_response(self, status_code, text_data):
        self.send_response(status_code)
        self.send_header('Content-type', 'text/plain; charset=utf-8')
        self.end_headers()
        self.wfile.write(text_data.encode('utf-8'))
        
def get_local_ip():
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    try:
        s.connect(('10.255.255.255', 1))
        IP = s.getsockname()[0]
    except Exception:
        IP = '127.0.0.1'
    finally:
        s.close()
    return IP

# ==============================================================================
# EXECUÇÃO DO SERVIDOR
# ==============================================================================
if __name__ == "__main__":
    PORT = 5000
    HOST_IP = '0.0.0.0' # Ouve em todas as interfaces de rede
    
    server_address = (HOST_IP, PORT)
    httpd = HTTPServer(server_address, APIServer)
    
    print("="*60)
    print("Servidor de API Integrado (v2 - Investidor10) iniciado!")
    print(f"Ouvindo em todas as interfaces na porta {PORT}")
    print(f"Acesse de outra máquina via: http://{get_local_ip()}:{PORT}")
    print("\n--- Exemplo para OPÇÕES ---")
    print(f"http://localhost:{PORT}/dados?code=BOVAJ28&field=strike-vencimento")
    print("\n--- Exemplo para DIVIDENDOS (NOVA FONTE) ---")
    print(f"http://localhost:{PORT}/dados?code=petr4&field=dividendos")
    print("\nPara parar o servidor, pressione CTRL+C.")
    print("="*60)
    
    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        pass
    
    httpd.server_close()
    print("\nServidor parado.")