import threading
from datetime import datetime
import logging
import src.database.connection as database
import src.database.utils as utils
from src.jobs.system_jobs import OnlineLogger
from src.database.utils import DatabaseConfig
import src.api.okvendas as api_okvendas
from src.api import slack
from src.database import queries
import src
import time
from threading import Lock

lock = Lock()
logger = logging.getLogger()
send_log = OnlineLogger.send_log


def job_insert_stock_semaphore(job_config_dict: dict):
    with lock:
        """
        Job para inserir estoques no banco semáforo
        Args:
            job_config_dict: Configuração do job
        """
        logger.info(job_config_dict.get('job_name') + f' | Executando na Thread {threading.current_thread()}')
        db_config = utils.get_database_config(job_config_dict)
        if db_config.sql is None:
            send_log(job_config_dict.get('job_name'), job_config_dict.get('enviar_logs'), False,
                     'Comando sql para inserir estoques no semaforo nao encontrado', 'warning', 'ESTOQUE')
        else:
            db = database.Connection(db_config)
            conn = db.get_conect()
            cursor = conn.cursor()

            try:
                send_log(job_config_dict.get('job_name'), job_config_dict.get('enviar_logs'), False,
                         f'Inserindo estoques no banco semaforo \n {db_config.sql}', 'info', 'ESTOQUE')
                cursor.execute(db_config.sql)
                send_log(job_config_dict.get('job_name'), job_config_dict.get('enviar_logs'), False,
                         f'{cursor.rowcount} estoques inseridos no banco semaforo', 'info', 'ESTOQUE')
            except Exception as ex:
                send_log(job_config_dict.get('job_name'), job_config_dict.get('enviar_logs'), True,
                         f'Erro ao inserir estoques no banco semaforo {str(ex)}', 'error', 'ESTOQUE')

            cursor.close()
            conn.commit()
            conn.close()



def job_send_stocks(job_config_dict: dict):
    with lock:
        """
        Job para realizar a atualização dos estoques padrão
        Args:
            job_config_dict: Configuração do job
        """
        logger.info(job_config_dict.get('job_name') + f' | Executando na Thread {threading.current_thread()}')
        db_config = utils.get_database_config(job_config_dict)
        stocks = query_stocks(job_config_dict, db_config)
        atualizados = []
        if len(stocks) or 0 > 0:
            send_log(job_config_dict.get('job_name'), job_config_dict.get('enviar_logs'), False,
                     'Total de estoques a serem atualizados: {len(stocks)}', 'info', '')
            for stock in stocks:
                try:
                    time.sleep(1)
                    response, status_code = api_okvendas.send_stocks(src.client_data.get('url_api') + '/catalogo/estoque',
                                                                     stock, src.client_data.get('token_api'))

                    if response is not None:
                        conexao = database.Connection(db_config)
                        conn = conexao.get_conect()
                        cursor = conn.cursor()

                        codigo_erp = response['codigo_erp']
                        if response['Status'] == 1 or response['Status'] == 'Success':
                            cursor.execute(queries.get_stock_protocol_command(db_config.db_type),
                                           queries.get_command_parameter(db_config.db_type, [codigo_erp]))
                            atualizados.append(response['codigo_erp'])
                        elif (response['Status'] == 'Error' or response['Status'] == 3) and response[
                            'Message'] == "Erro ao atualizar o estoque. Produto não existente":
                            send_log(job_config_dict.get('job_name'), job_config_dict.get('enviar_logs'), True,
                                     f'Erro ao atualizar estoque para o sku {codigo_erp}: {response["Message"]}', 'warning',
                                     'ESTOQUE', codigo_erp)
                            cursor.execute(queries.get_stock_protocol_command(db_config.db_type),
                                           queries.get_command_parameter(db_config.db_type, [codigo_erp]))
                        else:
                            send_log(job_config_dict.get('job_name'), job_config_dict.get('enviar_logs'), True,
                                     f'Erro ao atualizar estoque para o sku {codigo_erp}: {response["Message"]}', 'warning',
                                     'ESTOQUE', codigo_erp)

                        cursor.close()
                        conn.commit()
                        conn.close()
                except Exception as e:
                    send_log(job_config_dict.get('job_name'), job_config_dict.get('enviar_logs'), True,
                             f'Erro ao atualizar estoque do sku {stock.get("codigo_erp")}: {str(e)}', 'error', 'ESTOQUE',
                             stock.get('codigo_erp'))

            send_log(job_config_dict.get('job_name'), job_config_dict.get('enviar_logs'), False,
                     f'Atualizado estoques no semaforo: {len(atualizados) or 0}', 'info', 'ESTOQUE')
        else:
            send_log(job_config_dict.get('job_name'), job_config_dict.get('enviar_logs'), False,
                     f'Nao ha produtos para atualizar estoque no momento', 'warning', 'ESTOQUE')



def job_send_stocks_ud(job_config_dict: dict):
    with lock:
        """
        Job para realizar a atualização dos estoques por unidade de distribuição
        Args:
            job_config_dict: Configuração do job
        """
        logger.info(job_config_dict.get('job_name') + f' | Executando na Thread {threading.current_thread()}')
        db_config = utils.get_database_config(job_config_dict)
        stocks = query_stocks_ud(job_config_dict, db_config)
        p_size = len(stocks) if stocks is not None else 0

        if p_size > 0:
            time.sleep(0.5)
            send_log(job_config_dict.get('job_name'), job_config_dict.get('enviar_logs'), False,
                     f'Total de produtos para atualizar estoque: {p_size}', 'info', 'ESTOQUE')
            count = 0
            for stock in stocks:
                codigo_erp = stock["codigo_erp"]
                try:
                    response = api_okvendas.send_stocks_ud(
                        src.client_data.get('url_api') + '/catalogo/estoqueUnidadeDistribuicao', [stock],
                        src.client_data.get('token_api'))
                    for res in response:
                        if res.status == 1:
                            protocol_stock(job_config_dict, db_config, codigo_erp)
                            count = count + 1
                        elif (res.status == 'Error' or res.status > 1) and res.message.__contains__("não encontrado"):
                            send_log(job_config_dict.get('job_name'), job_config_dict.get('enviar_logs'), False,
                                     f'Erro ao atualizar estoque {codigo_erp} erro da api: {res.message}', 'warning',
                                     'ESTOQUE')
                            protocol_stock(job_config_dict, db_config, codigo_erp)
                        else:
                            send_log(job_config_dict.get('job_name'), job_config_dict.get('enviar_logs'), True,
                                     f'Erro ao atualizar estoque do sku {codigo_erp}: {res.message}', 'error', 'ESTOQUE',
                                     codigo_erp)
                except Exception as e:
                    send_log(job_config_dict.get('job_name'), job_config_dict.get('enviar_logs'), True,
                             f'Erro ao atualizar estoque para o sku {stock.get("codigo_erp")}: {str(e)}', 'error',
                             'ESTOQUE', stock.get('codigo_erp'))

            send_log(job_config_dict.get('job_name'), job_config_dict.get('enviar_logs'), False,
                     f'Produtos protocolados no semaforo: {count}', 'info', 'ESTOQUE')
        else:
            send_log(job_config_dict.get('job_name'), job_config_dict.get('enviar_logs'), False,
                     f'Nao ha produtos para atualizar estoque', 'warning', 'ESTOQUE')


def query_stocks_ud(job_config_dict: dict, db_config: DatabaseConfig):
    """
    Consulta no banco de dados os estoques pendentes de atualização por unidade de distribuição
    Args:
        job_config_dict: Configuração do job
        db_config: Configuração do banco de dados

    Returns:
    Lista de estoques para realizar a atualização
    """
    stocks = None
    if db_config.sql is None:
        send_log(job_config_dict.get('job_name'), job_config_dict.get('enviar_logs'), False,
                 f'Query estoque de produtos nao configurada', 'warning', 'ESTOQUE')
    else:
        try:
            conexao = database.Connection(db_config)
            conn = conexao.get_conect()
            cursor = conn.cursor()

            # print(db_config.sql)
            cursor.execute(db_config.sql)
            rows = cursor.fetchall()
            # print(rows)
            columns = [col[0] for col in cursor.description]
            results = [dict(zip(columns, row)) for row in rows]

            cursor.close()
            conn.close()
            if len(results) > 0:
                stocks = stock_ud_dict(results)

        except Exception as ex:
            send_log(job_config_dict.get('job_name'), job_config_dict.get('enviar_logs'), False, f'{str(ex)}', 'error',
                     'ESTOQUE')

    return stocks


def query_stocks(job_config_dict: dict, db_config: DatabaseConfig):
    """
    Consulta no banco de dados os estoques pendentes de atualização padrão
    Args:
        job_config_dict: Configuração do job
        db_config: Configuração do banco de dados

    Returns:
    Lista de estoques para realizar a atualização
    """
    produtos = None
    if db_config.sql is None or db_config.sql == '':
        slack.register_warn("Query estoque de produtos nao configurada!")
        send_log(job_config_dict.get('job_name'), job_config_dict.get('enviar_logs'), False,
                 f'Query estoque de produtos nao configurada', 'warning', 'ESTOQUE')
    else:
        try:
            conexao = database.Connection(db_config)
            conn = conexao.get_conect()
            cursor = conn.cursor()

            # print(db_config.sql)
            cursor.execute(db_config.sql)
            rows = cursor.fetchall()
            # print(rows)
            columns = [col[0] for col in cursor.description]
            results = [dict(zip(columns, row)) for row in rows]

            cursor.close()
            conn.close()
            if len(results) > 0:
                produtos = stock_dict(results)

        except Exception as ex:
            send_log(job_config_dict.get('job_name'), job_config_dict.get('enviar_logs'), False, f'{str(ex)}', 'error',
                     'ESTOQUE')

    return produtos


def stock_dict(produtos):
    lista = []
    for row in produtos:
        pdict = {
            'codigo_erp': str(row['CODIGO_ERP']),
            'quantidade': int(row['QUANTIDADE'])
        }
        lista.append(pdict)

    return lista


def stock_ud_dict(produtos):
    lista = []
    for row in produtos:
        pdict = {
            'unidade_distribuicao': row['CODIGO_UNIDADE_DISTRIBUICAO'],
            'codigo_erp': row['CODIGO_ERP'],
            'quantidade_total': int(row['QUANTIDADE']),
            'parceiro': 1
        }
        lista.append(pdict)

    return lista


def protocol_stock(job_config_dict, db_config: DatabaseConfig, codigo_erp):
    try:
        send_log(job_config_dict.get('job_name'), job_config_dict.get('enviar_logs'), False,
                 f'Protocolando produto {codigo_erp} no semaforo', 'info', 'ESTOQUE')
        conn = database.Connection(db_config).get_conect()
        cursor = conn.cursor()
        cursor.execute(queries.get_stock_protocol_command(db_config.db_type),
                       queries.get_command_parameter(db_config.db_type, [codigo_erp]))
        cursor.close()
        conn.commit()
        conn.close()
    except Exception as e:
        send_log(job_config_dict.get('job_name'), job_config_dict.get('enviar_logs'), False,
                 f'Erro ao protocolar estoque do sku {codigo_erp}: {str(e)}', 'error', '')
