from functions.database import get_db_connection, Militante, EmailMilitante, Endereco
from sqlalchemy.orm import joinedload
from datetime import datetime
from typing import List, Dict, Optional
from services.cache_service import cache_service, cached, CacheKeys, invalidate_cache_pattern
import logging

logger = logging.getLogger(__name__)

class MilitanteModel:
    """Model para operações com militantes"""
    
    @staticmethod
    @invalidate_cache_pattern("militantes:*")
    def criar_militante(data: Dict) -> Dict:
        """Cria um novo militante"""
        db = get_db_connection()
        try:
            # Criar endereço se fornecido
            endereco_id = None
            if data.get('endereco'):
                endereco = Endereco(**data['endereco'])
                db.add(endereco)
                db.flush()
                endereco_id = endereco.id
            
            # Criar militante
            militante = Militante(
                nome=data['nome'],
                cpf=data['cpf'],
                titulo_eleitoral=data.get('titulo_eleitoral'),
                data_nascimento=data.get('data_nascimento'),
                data_entrada_oci=data.get('data_entrada_oci'),
                data_efetivacao_oci=data.get('data_efetivacao_oci'),
                telefone1=data.get('telefone1'),
                telefone2=data.get('telefone2'),
                profissao=data.get('profissao'),
                regime_trabalho=data.get('regime_trabalho'),
                empresa=data.get('empresa'),
                contratante=data.get('contratante'),
                instituicao_ensino=data.get('instituicao_ensino'),
                tipo_instituicao=data.get('tipo_instituicao'),
                sindicato=data.get('sindicato'),
                cargo_sindical=data.get('cargo_sindical'),
                dirigente_sindical=data.get('dirigente_sindical', False),
                central_sindical=data.get('central_sindical'),
                endereco_id=endereco_id,
                celula_id=data.get('celula_id'),
                registrado_por=data.get('registrado_por')
            )
            
            db.add(militante)
            db.flush()
            
            # Criar email se fornecido
            if data.get('email'):
                email = EmailMilitante(
                    militante_id=militante.id,
                    endereco_email=data['email']
                )
                db.add(email)
            
            db.commit()
            
            # Cache the new militante
            cache_key = CacheKeys.militante_detail(militante.id)
            militante_data = MilitanteModel.formatar_dados_militante(militante)
            cache_service.set(cache_key, militante_data, 1800)  # 30 minutes
            
            logger.info(f"Militante {militante.id} criado e cacheado")
            
            return {
                'status': 'success',
                'message': 'Militante criado com sucesso',
                'militante_id': militante.id
            }
            
        except Exception as e:
            db.rollback()
            logger.error(f"Erro ao criar militante: {e}")
            return {
                'status': 'error',
                'message': f'Erro ao criar militante: {str(e)}'
            }
        finally:
            db.close()
    
    @staticmethod
    @cached(expire=1800, key_prefix="militantes")  # Cache for 30 minutes
    def listar_militantes() -> List[Militante]:
        """Lista todos os militantes"""
        db = get_db_connection()
        try:
            militantes = db.query(Militante).options(
                joinedload(Militante.emails),
                joinedload(Militante.endereco),
                joinedload(Militante.celula)
            ).order_by(Militante.nome).all()
            
            # Cache individual militantes
            for militante in militantes:
                cache_key = CacheKeys.militante_detail(militante.id)
                militante_data = MilitanteModel.formatar_dados_militante(militante)
                cache_service.set(cache_key, militante_data, 1800)
            
            logger.debug(f"Listados {len(militantes)} militantes e cacheados")
            return militantes
            
        except Exception as e:
            logger.error(f"Erro ao listar militantes: {e}")
            return []
        finally:
            db.close()
    
    @staticmethod
    def buscar_por_id(militante_id: int) -> Optional[Militante]:
        """Busca um militante por ID"""
        # Try cache first
        cache_key = CacheKeys.militante_detail(militante_id)
        cached_militante = cache_service.get(cache_key)
        
        if cached_militante:
            logger.debug(f"Cache hit para militante {militante_id}")
            # Convert cached data back to Militante object if needed
            return cached_militante
        
        # Cache miss, get from database
        db = get_db_connection()
        try:
            militante = db.query(Militante).options(
                joinedload(Militante.emails),
                joinedload(Militante.endereco)
            ).get(militante_id)
            
            if militante:
                # Cache the militante
                militante_data = MilitanteModel.formatar_dados_militante(militante)
                cache_service.set(cache_key, militante_data, 1800)
                logger.debug(f"Cache miss para militante {militante_id}, cacheado")
            
            return militante
            
        except Exception as e:
            logger.error(f"Erro ao buscar militante {militante_id}: {e}")
            return None
        finally:
            db.close()
    
    @staticmethod
    @invalidate_cache_pattern("militantes:*")
    def atualizar_militante(militante_id: int, data: Dict) -> Dict:
        """Atualiza um militante existente"""
        db = get_db_connection()
        try:
            militante = db.query(Militante).get(militante_id)
            
            if not militante:
                return {
                    'status': 'error',
                    'message': 'Militante não encontrado'
                }
            
            # Atualizar dados básicos
            militante.nome = data.get('nome', militante.nome)
            militante.cpf = data.get('cpf', militante.cpf)
            militante.titulo_eleitoral = data.get('titulo_eleitoral', militante.titulo_eleitoral)
            militante.telefone1 = data.get('telefone1', militante.telefone1)
            militante.telefone2 = data.get('telefone2', militante.telefone2)
            militante.profissao = data.get('profissao', militante.profissao)
            militante.regime_trabalho = data.get('regime_trabalho', militante.regime_trabalho)
            militante.empresa = data.get('empresa', militante.empresa)
            militante.contratante = data.get('contratante', militante.contratante)
            militante.instituicao_ensino = data.get('instituicao_ensino', militante.instituicao_ensino)
            militante.tipo_instituicao = data.get('tipo_instituicao', militante.tipo_instituicao)
            militante.sindicato = data.get('sindicato', militante.sindicato)
            militante.cargo_sindical = data.get('cargo_sindical', militante.cargo_sindical)
            militante.dirigente_sindical = data.get('dirigente_sindical', militante.dirigente_sindical)
            militante.central_sindical = data.get('central_sindical', militante.central_sindical)
            
            # Atualizar datas
            if data.get('data_nascimento'):
                militante.data_nascimento = data['data_nascimento']
            if data.get('data_entrada_oci'):
                militante.data_entrada_oci = data['data_entrada_oci']
            if data.get('data_efetivacao_oci'):
                militante.data_efetivacao_oci = data['data_efetivacao_oci']
            
            # Atualizar endereço
            if data.get('endereco') and militante.endereco:
                endereco = militante.endereco
                endereco.cep = data['endereco'].get('cep', endereco.cep)
                endereco.estado = data['endereco'].get('estado', endereco.estado)
                endereco.cidade = data['endereco'].get('cidade', endereco.cidade)
                endereco.bairro = data['endereco'].get('bairro', endereco.bairro)
                endereco.rua = data['endereco'].get('rua', endereco.rua)
                endereco.numero = data['endereco'].get('numero', endereco.numero)
                endereco.complemento = data['endereco'].get('complemento', endereco.complemento)
            
            # Atualizar email
            if data.get('email') and militante.emails:
                militante.emails[0].endereco_email = data['email']
            
            db.commit()
            
            # Update cache
            cache_key = CacheKeys.militante_detail(militante_id)
            militante_data = MilitanteModel.formatar_dados_militante(militante)
            cache_service.set(cache_key, militante_data, 1800)
            
            logger.info(f"Militante {militante_id} atualizado e cache atualizado")
            
            return {
                'status': 'success',
                'message': 'Militante atualizado com sucesso'
            }
            
        except Exception as e:
            db.rollback()
            logger.error(f"Erro ao atualizar militante {militante_id}: {e}")
            return {
                'status': 'error',
                'message': f'Erro ao atualizar militante: {str(e)}'
            }
        finally:
            db.close()
    
    @staticmethod
    @invalidate_cache_pattern("militantes:*")
    def excluir_militante(militante_id: int) -> Dict:
        """Exclui um militante"""
        db = get_db_connection()
        try:
            militante = db.query(Militante).get(militante_id)
            if not militante:
                return {
                    'status': 'error',
                    'message': 'Militante não encontrado'
                }
            
            db.delete(militante)
            db.commit()
            
            # Remove from cache
            cache_key = CacheKeys.militante_detail(militante_id)
            cache_service.delete(cache_key)
            
            logger.info(f"Militante {militante_id} excluído e removido do cache")
            
            return {
                'status': 'success',
                'message': 'Militante excluído com sucesso'
            }
            
        except Exception as e:
            db.rollback()
            logger.error(f"Erro ao excluir militante {militante_id}: {e}")
            return {
                'status': 'error',
                'message': f'Erro ao excluir militante: {str(e)}'
            }
        finally:
            db.close()
    
    @staticmethod
    @cached(expire=1800, key_prefix="militantes")
    def buscar_por_cpf(cpf: str) -> Optional[Militante]:
        """Busca um militante por CPF"""
        db = get_db_connection()
        try:
            militante = db.query(Militante).filter_by(cpf=cpf).first()
            if militante:
                # Cache the militante
                cache_key = CacheKeys.militante_detail(militante.id)
                militante_data = MilitanteModel.formatar_dados_militante(militante)
                cache_service.set(cache_key, militante_data, 1800)
            return militante
        except Exception as e:
            logger.error(f"Erro ao buscar militante por CPF {cpf}: {e}")
            return None
        finally:
            db.close()
    
    @staticmethod
    def formatar_dados_militante(militante: Militante) -> Dict:
        """Formata os dados de um militante para retorno JSON"""
        def formatar_data_segura(data):
            try:
                if not data:
                    return None
                return data.strftime('%Y-%m-%d')
            except Exception as e:
                logger.error(f"Erro ao formatar data: {str(e)}, valor: {data}")
                return None
        
        return {
            'id': militante.id,
            'nome': militante.nome,
            'cpf': militante.cpf,
            'titulo_eleitoral': militante.titulo_eleitoral,
            'data_nascimento': formatar_data_segura(militante.data_nascimento),
            'data_entrada_oci': formatar_data_segura(militante.data_entrada_oci),
            'data_efetivacao_oci': formatar_data_segura(militante.data_efetivacao_oci),
            'telefone1': militante.telefone1,
            'telefone2': militante.telefone2,
            'profissao': militante.profissao,
            'regime_trabalho': militante.regime_trabalho,
            'empresa': militante.empresa,
            'contratante': militante.contratante,
            'instituicao_ensino': militante.instituicao_ensino,
            'tipo_instituicao': militante.tipo_instituicao,
            'sindicato': militante.sindicato,
            'cargo_sindical': militante.cargo_sindical,
            'dirigente_sindical': militante.dirigente_sindical,
            'central_sindical': militante.central_sindical,
            'responsabilidades': militante.responsabilidades,
            'estado': militante.estado.value if militante.estado else None,
            'celula_id': militante.celula_id,
            'email': militante.emails[0].endereco_email if militante.emails else None,
            'endereco': {
                'cep': militante.endereco.cep if militante.endereco else None,
                'estado': militante.endereco.estado if militante.endereco else None,
                'cidade': militante.endereco.cidade if militante.endereco else None,
                'bairro': militante.endereco.bairro if militante.endereco else None,
                'rua': militante.endereco.rua if militante.endereco else None,
                'numero': militante.endereco.numero if militante.endereco else None,
                'complemento': militante.endereco.complemento if militante.endereco else None
            } if militante.endereco else None
        } 