import numpy as np
import matplotlib.pyplot as plt
from collections import Counter
from itertools import groupby
from scipy import stats

# use: pip install scipy

# Constantes
BLOCK = "block_integer_array.npy"
SIGNIFICANCE_LEVEL = 0.05

def load_data(filename):
    """Carrega os dados do arquivo especificado."""
    try:
        return np.load(filename)
    except FileNotFoundError:
        print(f"Erro: O arquivo {filename} não foi encontrado.")
        return None

def split_into_months(data_array):
    """Divide o array de dados em 12 partes, uma para cada mês.
    Porém, se o número de elementos não for divisível por 12, seria necessário
    adaptar essa função para redimensionar os dados para o maior múltiplo de 12."""

    trunc_len = (len(data_array) // 360) * 360
    return data_array[:trunc_len].reshape(-1, 30)
    # return np.array_split(data_array, 12)

def get_month(monthly_data, month):
    return monthly_data[month - 1]  # 0-indexado

# def plot_data(monthly_data):
#     plt.hist(monthly_data, bins=30)
#     plt.title("Poder computacional por dia em Novembro")
#     plt.show()

def plot_data(monthly_data):
    # Extrai os dias e o poder computacional
    days = range(1, len(monthly_data) + 1)
    power = monthly_data

    plt.figure(figsize=(6, 4))
    plt.plot(days, power, alpha=0.7)
    plt.title("Poder computacional por dia em Novembro")
    plt.xlabel("Dia")
    plt.ylabel("Poder Computacional")
    plt.show()

def get_most_powerful_miner(monthly_data):
    """Retorna o minerador mais poderoso com base nos dados mensais fornecidos,
    a partir do primeiro elemento da primeira tupla (mais comum) da lista
    de tuplas retornada pelo método most_common do objeto Counter."""
    counter = Counter(monthly_data)
    return counter.most_common(1)[0][0]

def count_sequences(month_data, miner):
    """Conta o número de sequências consecutivas de mineração realizadas pelo minerador
    especificado (mp). O método groupby do módulo itertools é usado para agrupar os elementos
    consecutivos do array de dados"""
    return [sum(1 for _ in group) for key, group in groupby(month_data) if key == miner]

def generate_permutations(data, miner, num_permutations=1000):
    """Gera permutações aleatórias dos dados e conta o número de sequências consecutivas
    de mineração realizadas pelo mp em cada permutação."""
    perm_sequences = []
    for _ in range(num_permutations):
        perm = np.random.permutation(data)
        perm_sequences.append(count_sequences(perm, miner))
    return perm_sequences

def calc_pvalue(sequences, perm_sequences):
    """Calcula o p-value para a sequência de mineração do minerador mais poderoso
    com base nas permutações geradas e retorna o percentil do valor máximo da sequência
    de mineração do mp em relação às permutações."""
    max_perm_sequences = [max(seq) for seq in perm_sequences]
    max_sequences = max(sequences)
    return stats.percentileofscore(max_perm_sequences, max_sequences)

def print_conclusion(pvalue, significance_level):
    pvalue_rounded = round(pvalue, 2)
    print(f"p-value: {pvalue_rounded}")
    if pvalue < significance_level:
        print(f"Mineração egoísta provavelmente ocorreu (p-value: {pvalue_rounded} < {significance_level}).\\nA sequência de minerações do minerador mais poderoso é significativamente mais longa do que o esperado por acaso, isto é, diferente do que seria esperado se hipótese nula fosse verdadeira.")
    else:
        print(f"Mineração egoísta provavelmente não ocorreu (p-value: {pvalue_rounded} >= {significance_level}).\\nA sequência de minerações do minerador mais poderoso não é significativamente\\nmais longa do que o esperado por acaso, isto é, não é diferente do que seria esperado se hipótese nula fosse verdadeira.")

def main():
    data_array = load_data(BLOCK)
    monthly_data = split_into_months(data_array)
    # november = get_month(monthly_data, 11)
    november = monthly_data[10]  # 0-indexed
    plot_data(november)
    mp = get_most_powerful_miner(november)
    sequences = count_sequences(november, mp)
    perm_sequences = generate_permutations(november, mp)
    pvalue = calc_pvalue(sequences, perm_sequences)
    print_conclusion(pvalue, SIGNIFICANCE_LEVEL)

if __name__ == "__main__":
    main()