#!/usr/bin/env python3
"""
Preditor de glosas para entrada JSON
Analisa guias individuais ou em lote via JSON
"""

import json
import pandas as pd
import numpy as np
import joblib
import os
import sys
import warnings
from datetime import datetime
warnings.filterwarnings('ignore')

class PreditorJSON:
    def __init__(self):
        self.tipos_guia = {
            1: 'Internação',
            2: 'SADT',
            3: 'Consulta',
            4: 'Honorários',
            5: 'Odonto'
        }
        self.modelos = {}
        self.modelos_carregados = False
        
    def carregar_modelos(self):
        """Carrega modelos disponíveis"""
        if self.modelos_carregados:
            return True
            
        print("🔄 Carregando modelos...")
        
        # Modelo geral
        if os.path.exists('modelo_export_guias_2025-07-05_23-04-07.pkl'):
            self.modelos['geral'] = joblib.load('modelo_export_guias_2025-07-05_23-04-07.pkl')
            print("   ✅ Modelo geral carregado")
        
        # Modelos por tipo
        for tipo in self.tipos_guia.keys():
            import glob
            arquivos = glob.glob(f'modelo_*_tipo{tipo}.pkl')
            if arquivos:
                self.modelos[f'tipo_{tipo}'] = joblib.load(arquivos[0])
                print(f"   ✅ Modelo {self.tipos_guia[tipo]} carregado")
        
        # Modelo avançado de consulta
        if os.path.exists('modelo_consulta_avancado.pkl'):
            self.modelos['consulta_avancado'] = joblib.load('modelo_consulta_avancado.pkl')
            print("   ✅ Modelo avançado de Consulta carregado")
        
        if not self.modelos:
            print("❌ Nenhum modelo encontrado!")
            return False
            
        self.modelos_carregados = True
        return True
    
    def preparar_dados(self, df):
        """Prepara dados do DataFrame"""
        # Converte datas
        for col in ['data_cadastro', 'data_cirurgia', 'data_envio_operadora']:
            if col in df.columns:
                df[col] = pd.to_datetime(df[col], errors='coerce')
        
        # Features temporais
        if 'data_envio_operadora' in df.columns and 'data_cadastro' in df.columns:
            df['dias_envio'] = (df['data_envio_operadora'] - df['data_cadastro']).dt.days
            df['dias_envio'] = df['dias_envio'].fillna(5)
        else:
            df['dias_envio'] = 5
        
        if 'data_cirurgia' in df.columns and 'data_cadastro' in df.columns:
            df['dias_ate_cirurgia'] = (df['data_cirurgia'] - df['data_cadastro']).dt.days
            df['dias_ate_cirurgia'] = df['dias_ate_cirurgia'].fillna(0)
        else:
            df['dias_ate_cirurgia'] = 0
        
        if 'data_cirurgia' in df.columns:
            df['mes_cirurgia'] = df['data_cirurgia'].dt.month
            df['dia_semana_cirurgia'] = df['data_cirurgia'].dt.dayofweek
            df['mes_cirurgia'] = df['mes_cirurgia'].fillna(6)
            df['dia_semana_cirurgia'] = df['dia_semana_cirurgia'].fillna(3)
        else:
            df['mes_cirurgia'] = 6
            df['dia_semana_cirurgia'] = 3
        
        # Procedimentos combinados
        if 'procedimentos_combinados' in df.columns:
            df['procedimentos_combinados'] = df['procedimentos_combinados'].fillna('').astype(str).str.count(',') + 1
            df.loc[df['procedimentos_combinados'] == 1, 'procedimentos_combinados'] = 0
        
        return df
    
    def predizer_guia(self, guia_data, modelo_info):
        """Prediz uma única guia"""
        model = modelo_info['model']
        encoders = modelo_info['encoders']
        best_threshold = modelo_info['best_threshold']
        feature_names = modelo_info['feature_names']
        
        # Converte para DataFrame
        df = pd.DataFrame([guia_data])
        df = self.preparar_dados(df)
        
        # Codifica categóricas
        for col, encoder in encoders.items():
            if col in df.columns:
                df[col] = df[col].fillna('Unknown').astype(str)
                try:
                    df[col] = encoder.transform(df[col])
                except:
                    # Valor não visto no treino
                    df[col] = 0
        
        # Features específicas para consulta avançada
        if 'convenios_risco' in modelo_info:
            df['convenio_risco'] = df['id_convenio'].isin(modelo_info['convenios_risco']).astype(int)
            
            # Agrupa procedimentos raros
            if 'id_cod_agrupado' not in df.columns:
                df['id_cod_agrupado'] = df['id_cod']  # Simplificado para JSON
        
        # Remove glosado se existir
        if 'glosado' in df.columns:
            df = df.drop('glosado', axis=1)
        
        # Prepara features
        X = pd.DataFrame()
        for feat in feature_names:
            if feat in df.columns:
                X[feat] = df[feat]
            else:
                X[feat] = 0
        
        # Garante tipos numéricos
        for col in X.columns:
            if X[col].dtype == 'object':
                X[col] = pd.to_numeric(X[col], errors='coerce').fillna(0)
        
        # Predição
        if 'model_focal' in modelo_info:  # Modelo avançado de consulta
            # Predição ensemble
            proba_focal = modelo_info['model_focal'].predict_proba(X)[:, 1][0]
            
            proba_cv = 0
            for cv_model in modelo_info['cv_models']:
                proba_cv += cv_model.predict_proba(X)[:, 1][0]
            proba_cv /= len(modelo_info['cv_models'])
            
            probabilidade = modelo_info['weights']['focal'] * proba_focal + modelo_info['weights']['cv'] * proba_cv
        else:
            # Modelo simples
            probabilidade = model.predict_proba(X)[:, 1][0]
        
        predicao = int(probabilidade >= best_threshold)
        
        # Nível de risco
        if probabilidade < 0.3:
            nivel_risco = 'Baixo'
        elif probabilidade < 0.5:
            nivel_risco = 'Médio'
        elif probabilidade < 0.7:
            nivel_risco = 'Alto'
        else:
            nivel_risco = 'Muito Alto'
        
        return {
            'probabilidade_glosa': float(probabilidade),
            'predicao_glosa': predicao,
            'nivel_risco': nivel_risco,
            'threshold_usado': float(best_threshold)
        }
    
    def analisar_json(self, json_data):
        """Analisa dados JSON"""
        if not self.carregar_modelos():
            return None
        
        # Converte para lista se for dict único
        if isinstance(json_data, dict):
            json_data = [json_data]
        
        print(f"\n📊 Analisando {len(json_data)} registro(s)...")
        
        resultados = []
        
        # Agrupa por protocolo
        protocolos = {}
        for item in json_data:
            protocolo = item.get('protocolo', 'sem_protocolo')
            if protocolo not in protocolos:
                protocolos[protocolo] = []
            protocolos[protocolo].append(item)
        
        print(f"📋 {len(protocolos)} protocolo(s) único(s) encontrado(s)")
        
        # Analisa cada protocolo
        for protocolo, itens in protocolos.items():
            print(f"\n🔍 Protocolo: {protocolo}")
            print(f"   • {len(itens)} procedimento(s)")
            
            # Identifica tipo de guia
            tipo_guia = itens[0].get('tipo_guia', 0)
            tipo_nome = self.tipos_guia.get(tipo_guia, 'Desconhecido')
            print(f"   • Tipo: {tipo_nome}")
            
            # Seleciona modelo apropriado
            modelo_key = None
            if tipo_guia == 3 and 'consulta_avancado' in self.modelos:
                modelo_key = 'consulta_avancado'
                print("   • Usando modelo AVANÇADO de consulta")
            elif f'tipo_{tipo_guia}' in self.modelos:
                modelo_key = f'tipo_{tipo_guia}'
                print(f"   • Usando modelo específico para {tipo_nome}")
            else:
                modelo_key = 'geral'
                print("   • Usando modelo geral")
            
            if modelo_key not in self.modelos:
                print("   ❌ Modelo não disponível")
                continue
            
            modelo_info = self.modelos[modelo_key]
            
            # Analisa cada procedimento
            procedimentos_resultado = []
            scores = []
            
            for item in itens:
                resultado = self.predizer_guia(item, modelo_info)
                scores.append(resultado['probabilidade_glosa'])
                
                procedimento_info = {
                    'id': item.get('id'),
                    'id_cod': item.get('id_cod'),
                    'cod': item.get('cod'),
                    'quantidade': item.get('quantidade', 1),
                    'via_acesso': item.get('via_acesso'),
                    **resultado
                }
                procedimentos_resultado.append(procedimento_info)
            
            # Análise consolidada do protocolo
            score_maximo = max(scores)
            score_medio = np.mean(scores)
            algum_risco = any(s > modelo_info['best_threshold'] for s in scores)
            
            # Formato simplificado para o resultado
            analise_procedimentos = []
            for proc in procedimentos_resultado:
                analise_procedimentos.append({
                    str(proc['cod']): round(proc['probabilidade_glosa'], 2)
                })
            
            protocolo_resultado = {
                'protocolo': protocolo,
                'tipo_guia': tipo_nome,
                'analise_procedimentos': analise_procedimentos,
                'resultado': {
                    'media': round(score_medio, 2),
                    'maximo': round(score_maximo, 2),
                    'risco': self._calcular_nivel_risco(score_maximo).lower(),
                    'predicao': 'glosa_provavel' if algum_risco else 'aprovado'
                }
            }
            
            resultados.append(protocolo_resultado)
            
            # Resumo do protocolo
            print(f"\n   📈 Resultado da Análise:")
            print(f"      • Probabilidade máxima de glosa: {score_maximo:.1%}")
            print(f"      • Probabilidade média de glosa: {score_medio:.1%}")
            print(f"      • Classificação de risco: {protocolo_resultado['nivel_risco_protocolo']}")
            print(f"      • Predição: {'GLOSA PROVÁVEL' if algum_risco else 'Aprovado'}")
            
            if algum_risco:
                print(f"\n   ⚠️  Procedimentos de risco:")
                for proc in procedimentos_resultado:
                    if proc['probabilidade_glosa'] > modelo_info['best_threshold']:
                        print(f"      • Cod {proc['cod']}: {proc['probabilidade_glosa']:.1%} de risco")
        
        return resultados
    
    def _calcular_nivel_risco(self, score):
        """Calcula nível de risco baseado no score"""
        if score < 0.3:
            return 'Baixo'
        elif score < 0.5:
            return 'Médio'
        elif score < 0.7:
            return 'Alto'
        else:
            return 'Muito Alto'
    
    def processar_arquivo(self, arquivo_json):
        """Processa arquivo JSON"""
        print(f"\n📂 Lendo arquivo: {arquivo_json}")
        
        try:
            with open(arquivo_json, 'r', encoding='utf-8') as f:
                json_data = json.load(f)
        except Exception as e:
            print(f"❌ Erro ao ler arquivo: {e}")
            return None
        
        return self.analisar_json(json_data)
    
    def processar_string(self, json_string):
        """Processa string JSON"""
        try:
            json_data = json.loads(json_string)
        except Exception as e:
            print(f"❌ Erro ao interpretar JSON: {e}")
            return None
        
        return self.analisar_json(json_data)


def main():
    print("\n🤖 PREDITOR DE GLOSAS - Entrada JSON")
    print("="*60)
    
    if len(sys.argv) < 2:
        print("\n📌 Como usar:")
        print("   python predizer_json.py <arquivo.json>")
        print("   python predizer_json.py '<json_string>'")
        print("\n📌 Exemplos:")
        print("   python predizer_json.py guia.json")
        print('   python predizer_json.py \'{"id": 123, "tipo_guia": 1, ...}\'')
        print("\n📌 Formato JSON esperado:")
        print("   - Objeto único ou array de objetos")
        print("   - Campos: id, protocolo, tipo_guia, id_convenio, etc.")
        sys.exit(1)
    
    entrada = sys.argv[1]
    preditor = PreditorJSON()
    
    # Verifica se é arquivo ou string
    if os.path.exists(entrada):
        resultados = preditor.processar_arquivo(entrada)
    else:
        # Tenta como string JSON
        resultados = preditor.processar_string(entrada)
    
    if resultados:
        # Salva resultados
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        arquivo_saida = f"predicao_json_{timestamp}.json"
        
        with open(arquivo_saida, 'w', encoding='utf-8') as f:
            json.dump(resultados, f, indent=2, ensure_ascii=False)
        
        print(f"\n💾 Resultados salvos em: {arquivo_saida}")
        
        # Resumo final
        print("\n" + "="*60)
        print("📊 RESUMO GERAL")
        print("="*60)
        
        total_protocolos = len(resultados)
        protocolos_risco = sum(1 for r in resultados if r['predicao_protocolo'])
        
        print(f"\n• Protocolos analisados: {total_protocolos}")
        print(f"• Protocolos com risco de glosa: {protocolos_risco} de {total_protocolos}")
        
        # Por tipo
        print("\n📋 Por tipo de guia:")
        tipos_count = {}
        for r in resultados:
            tipo = r['tipo_nome']
            if tipo not in tipos_count:
                tipos_count[tipo] = {'total': 0, 'risco': 0}
            tipos_count[tipo]['total'] += 1
            if r['predicao_protocolo']:
                tipos_count[tipo]['risco'] += 1
        
        for tipo, counts in tipos_count.items():
            if counts['total'] > 0:
                print(f"   • {tipo}: {counts['risco']} de {counts['total']} protocolo(s) com risco")


if __name__ == "__main__":
    main()