import json
import requests
from tqdm import tqdm
from datetime import datetime
from django.db.models import F
from djtools.utils import mask_cpf
from edu.models import CursoCampus, MatriculaPeriodo
from .models import Configuracao, Raca, Turno, Cota, Programa, Curso, Ciclo, Vaga
from comum.models import Configuracao as ConfiguracaoSUAP

TIPOS_INCONSISTENCIA = {
    1: 'Associação ao Catálogo',
    2: 'Nome do Curso',
    3: 'Evasão 0%',
    4: 'Carga Horária Insuficiente',
    5: 'Programas Associados',
    6: 'Duração do Ciclo',
    7: 'Número de vagas',
    8: 'Ingressantes > Inscritos',
    9: 'Turno de Oferta do Ciclo',
    10: 'Matrícula Anterior',
    11: 'Matrícula Posterior',
    12: 'Aluno Duplicado',
    13: 'Retenção Crítica',
    14: 'Retenção FIC',
    15: 'Cor/Raça',
    16: 'Renda',
    17: 'Turno do Aluno',
    27: 'Habilitação Ciclo',
    29: 'Forma de Ingresso',
    28: 'Habilitação Matrícula',
}

SITUACAO_INCONSISTENCIA = {
    1: 'Inconsistente RA',
    2: 'Alterado RA',
}


class PnpService:
    def __init__(self, user, *args, **kwargs):
        self.url = ConfiguracaoSUAP.get_valor_por_chave('pnp_ccv', 'url')
        token = ConfiguracaoSUAP.get_valor_por_chave('pnp_ccv', 'token')
        cpf = user.get_profile().cpf.replace('.', '').replace('-', '')
        self.headers = {'X-API-KEY': cpf, 'Authorization': f'Bearer {token}'}

    def sincronizacao_inicial(self):
        url = f'{self.url}/api/v2/integracao/sincronizacao_inicial'
        dados = requests.get(url, headers=self.headers, timeout=60).json()
        Configuracao.objects.update_or_create(
            ano=dados['configuracao']['ano'],
            defaults={
                'data_inicio': datetime.strptime(dados['configuracao']['data_inicio'], '%d/%m/%Y'),
                'data_fim': datetime.strptime(dados['configuracao']['data_fim'], '%d/%m/%Y'),
                'data_envio': datetime.strptime(dados['configuracao']['data_envio'], '%d/%m/%Y'),
                'data_inicio_correcao': datetime.strptime(dados['configuracao']['data_inicio_correcao'], '%d/%m/%Y'),
                'data_fim_correcao': datetime.strptime(dados['configuracao']['data_fim_correcao'], '%d/%m/%Y'),
            },
        )
        for item in dados['racas']:
            Raca.objects.update_or_create(codigo=item['codigo'], defaults={'nome': item['nome']})
        for item in dados['turnos']:
            Turno.objects.update_or_create(codigo=item['codigo'], defaults={'nome': item['nome']})
        for item in dados['programas']:
            Programa.objects.update_or_create(codigo=item['codigo'], defaults={'nome': item['nome']})
        for item in dados['cotas']:
            Cota.objects.update_or_create(codigo=item['codigo'], defaults={'nome': item['nome']})

    def unidades_usuario(self):
        url = f'{self.url}/api/v2/integracao/unidades_usuario'
        response = requests.get(url, headers=self.headers, timeout=60)
        if response.status_code == 200:
            return response.json()
        return []

    def arquivos_integracao(self):
        url = f'{self.url}/api/v2/integracao/arquivos_integracao'
        response = requests.get(url, headers=self.headers, timeout=60)
        if response.status_code == 200:
            return response.json()
        return []

    def arquivo_integracao(self, codigo):
        url = f'{self.url}/api/v2/integracao/arquivo_integracao/{codigo}'
        response = requests.get(url, headers=self.headers, timeout=60)
        if response.status_code == 200:
            dados = response.json()
            dados['tabela_pre_processamento'] = tabela_totais(dados['totais_inicio_pre_processamento'], dados['totais_termino_pre_processamento'])
            dados['tabela_processamento'] = tabela_totais(dados['totais_inicio_processamento'], dados['totais_termino_processamento'])
            if dados['resultado_pre_processamento']:
                for item in dados['resultado_pre_processamento']['erros']:
                    if item['tipo']:
                        item['tipo'] = TIPOS_INCONSISTENCIA[item['tipo']]
                for item in dados['resultado_pre_processamento']['correcoes']:
                    item['tipo'] = TIPOS_INCONSISTENCIA[item['tipo']]
            if dados['resultado_processamento']:
                for item in dados['resultado_processamento']['erros']:
                    if item['tipo']:
                        item['tipo'] = TIPOS_INCONSISTENCIA[item['tipo']]
                for item in dados['resultado_processamento']['correcoes']:
                    item['tipo'] = TIPOS_INCONSISTENCIA[item['tipo']]
            return dados
        return None

    def importar_dados(self, codigo_unidade, task=None):
        cotas = Cota.objects.all()
        url = f'{self.url}/api/v2/integracao/exportar_dados_unidade/{codigo_unidade}'
        dados = requests.get(url, headers=self.headers, timeout=60).json()[0:1]
        # dados = json.loads(open("natal.json").read())
        for dados_curso in task.iterate(dados) if task else tqdm(dados):
            curso_campus = CursoCampus.objects.filter(codigo_sistec=dados_curso['codigo']).first()
            if curso_campus is None:
                continue
            curso = Curso.objects.update_or_create(
                codigo=dados_curso['codigo'],
                defaults={
                    'nome': dados_curso['nome'],
                    'codigo_catalogo': dados_curso['codigo_catalogo'],
                    'codigo_unidade': dados_curso['codigo_unidade'],
                    'curso_campus': curso_campus,
                },
            )[0]
            for dados_ciclo in dados_curso['ciclos'][0:1]:
                ciclo = Ciclo.objects.update_or_create(
                    codigo=dados_ciclo['codigo'],
                    defaults={
                        'curso': curso,
                        'nome': dados_ciclo['nome'],
                        'matriculas': dados_ciclo['alunos'],
                        'minicurso': curso_campus.ch_total is not None,
                    },
                )[0]
                cpfs = [mask_cpf(dados_aluno['cpf']) for dados_aluno in dados_ciclo['alunos'] if dados_aluno['cpf'] and len(dados_aluno['cpf']) == 11]
                qs = MatriculaPeriodo.objects.filter(
                    aluno__curso_campus=curso_campus,
                    aluno__pessoa_fisica__cpf__in=cpfs,
                    ano_letivo=F('aluno__ano_letivo'),
                    periodo_letivo=F('aluno__periodo_letivo'),
                )
                if curso_campus.ch_total is None:
                    # turmas regulares
                    turmas = (
                        qs.filter(turma__curso_campus=curso_campus, turma__periodo_matriz=1)
                        .order_by('turma')
                        .values_list('turma', flat=True)
                        .distinct()
                    )
                    ciclo.turmas.set(turmas)
                    for cota in cotas:
                        Vaga.objects.get_or_create(ciclo=ciclo, cota=cota, defaults={})
                else:
                    # turmas de minicurso
                    turmas_minicurso = (
                        qs.filter(aluno__participanteturmaminicurso__turma__isnull=False)
                        .order_by('aluno__participanteturmaminicurso__turma')
                        .values_list('aluno__participanteturmaminicurso__turma', flat=True)
                        .distinct()
                    )
                    ciclo.turmas_minicurso.set(turmas_minicurso)
                    for cota in cotas:
                        defaults = {'regular': len(turmas_minicurso)} if cota.nome == 'AC' else {}
                        Vaga.objects.get_or_create(ciclo=ciclo, cota=cota, defaults=defaults)
        if task:
            task.finalize('Importação realizada com sucesso.', '/admin/pnp_ccv/configuracao/')

    def processar_dados(self, codigo_arquivo):
        url = f'{self.url}/api/v2/integracao/processar_arquivo_integracao/{codigo_arquivo}'
        response = requests.get(url, headers=self.headers, timeout=60)
        return response.status_code == 200

    def exportar_dados(self, codigo_unidade, task=None):
        cursos = Curso.objects.filter(codigo_unidade=codigo_unidade)
        dados = [
            {
                'codigo_curso': curso.codigo,
                'codigo_catalogo': curso.codigo_catalogo,
                'ciclos': [
                    {
                        'codigo': ciclo.codigo,
                        'data_inicio': ciclo.get_data_inicio().strftime('%d/%m/%Y'),
                        'data_termino': ciclo.get_data_termino().strftime('%d/%m/%Y'),
                        'carga_horaria': ciclo.get_carga_horaria(),
                        'programa': ciclo.get_programa(),
                        'turnos': ciclo.get_turnos(lista=True),
                        'vagas': [
                            {'cota': sigla, 'regular': qtd, 'extraordinaria': 0} for sigla, qtd in ciclo.get_detalhamento_vagas()['vagas'].items()
                        ],
                        'inscritos': ciclo.get_total_inscritos(),
                        'ingressantes': ciclo.get_ingressantes().count(),
                        'evadidos': ciclo.get_evadidos().count(),
                        'matriculas': [
                            {
                                'codigo': aluno['codigo_matricula'],
                                'data': aluno['data_matricula'].strftime('%d/%m/%Y'),
                                'situacao': aluno['situacao']['pnp']['descricao'] if aluno['situacao'] else None,
                                'data_situacao': aluno['situacao']['pnp']['data'].strftime('%d/%m/%Y') if aluno['situacao'] else None,
                                'raca': aluno['raca'],
                                'renda': aluno['renda'],
                                'turno': aluno['turno'],
                                'cota': aluno['cota'],
                            }
                            for aluno in ciclo.get_dados_alunos()
                        ],
                    }
                    for ciclo in curso.ciclo_set.all()
                    if ciclo.turmas.exists() or ciclo.turmas_minicurso.exists()
                ],
            }
            for curso in (task.iterate(cursos) if task else tqdm(cursos))
        ]
        with open(f'{codigo_unidade}.json', 'w') as file:
            file.write(json.dumps(dados, indent=1, ensure_ascii=False))

        url = f'{self.url}/api/v2/integracao/importar_dados_unidade/{codigo_unidade}'
        with open(f'{codigo_unidade}.json') as file:
            requests.post(url, files={'file': file}, headers=self.headers, timeout=60)
        if task:
            task.finalize('Dados exportados com sucesso.')


def tabela_totais(totais1, totais2):
    rows = []
    header = []
    header.append('')
    index1 = {(item['tipo'], item['situacao']): item['total'] for item in totais1 or ()}
    index2 = {(item['tipo'], item['situacao']): item['total'] for item in totais2 or ()}
    for situacao in SITUACAO_INCONSISTENCIA.values():
        header.append(situacao)  # noqa: PERF402
    for id_tipo, tipo in TIPOS_INCONSISTENCIA.items():
        row = [tipo]
        for id_situacao in SITUACAO_INCONSISTENCIA:
            total1 = index1.get((id_tipo, id_situacao), 0)
            total2 = index2.get((id_tipo, id_situacao), 0)
            row.append(f'{total1} → {total2}')
        rows.append(row)
    return {'header': header, 'rows': rows}
