import agentpy as ap
import pandas as pd
import numpy as np
import networkx as nx
from firm import FirmAgent

sample = 0
seed = 0
n_iter = 3
list_init_remove_firm_code = [0, 2]
dct_sample_para = {
    'sample': sample,
    'seed': seed,
    'n_iter': n_iter,
    'list_init_remove_firm_code': list_init_remove_firm_code
}


class Model(ap.Model):
    def setup(self):
        self.sample = self.p.sample
        self.nprandom = np.random.default_rng(self.p.seed)
        self.int_n_iter = int(self.p.n_iter)

        # init graph bom
        BomNodes = pd.read_csv('BomNodes.csv', index_col=0)
        BomNodes.set_index('Code', inplace=True)
        BomCateNet = pd.read_csv('BomCateNet.csv', index_col=0)
        BomCateNet.fillna(0, inplace=True)

        G_bom = nx.from_pandas_adjacency(BomCateNet,
                                         create_using=nx.MultiDiGraph())

        bom_labels_dict = {}
        for code in G_bom.nodes:
            bom_labels_dict[code] = BomNodes.loc[code].to_dict()
        nx.set_node_attributes(G_bom, bom_labels_dict)

        # init graph firm
        Firm = pd.read_csv("Firm_amended.csv")
        Firm.fillna(0, inplace=True)
        Firm_attr = Firm.loc[:, ["Code", "Name", "Type_Region", "Revenue_Log"]]
        firm_product = []
        for _, row in Firm.loc[:, '1':].iterrows():
            firm_product.append(row[row == 1].index.to_list())
        Firm_attr.loc[:, 'Product_Code'] = firm_product
        Firm_attr.set_index('Code')
        G_Firm = nx.MultiDiGraph()
        G_Firm.add_nodes_from(Firm["Code"])

        firm_labels_dict = {}
        for code in G_Firm.nodes:
            firm_labels_dict[code] = Firm_attr.loc[code].to_dict()
        nx.set_node_attributes(G_Firm, firm_labels_dict)

        # add edge to G_firm according to G_bom
        for node in nx.nodes(G_Firm):
            # print(node, '-'*20)
            list_pred_product_code = []
            for product_code in G_Firm.nodes[node]['Product_Code']:
                list_pred_product_code += list(
                    G_bom.predecessors(product_code))
            list_pred_product_code = list(set(list_pred_product_code))
            for pred_product_code in list_pred_product_code:
                # print(pred_product_code)
                list_pred_firms = Firm.index[Firm[pred_product_code] ==
                                             1].to_list()
                list_revenue_log = [
                    G_Firm.nodes[pred_firm]['Revenue_Log']
                    for pred_firm in list_pred_firms
                ]
                list_prob = [
                    (v - min(list_revenue_log) + 1) /
                    (max(list_revenue_log) - min(list_revenue_log) + 1)
                    for v in list_revenue_log
                ]
                list_flag = [
                    self.nprandom.choice([1, 0], p=[prob, 1 - prob])
                    for prob in list_prob
                ]
                # print(list(zip(list_pred_firms,list_flag, list_prob)))
                list_added_edges = [
                    (node, pred_firm)
                    for pred_firm, flag in zip(list_pred_firms, list_flag)
                    if flag == 1
                ]
                G_Firm.add_edges_from(list_added_edges)
                # print('-'*20)

        self.firm_network = ap.Network(self, G_Firm)
        # print([node.label for node in self.firm_network.nodes])
        # print([list(self.firm_network.graph.predecessors(node)) for node in self.firm_network.nodes])
        # print([self.firm_network.graph.nodes[node.label]['Name'] for node in self.firm_network.nodes])
        # print([v for v in self.firm_network.graph.nodes(data=True)])

        # init firm
        for ag_node, attr in self.firm_network.graph.nodes(data=True):
            firm_agent = FirmAgent(self,
                                   code=attr['Code'],
                                   name=attr['Name'],
                                   type_region=attr['Type_Region'],
                                   revenue_log=attr['Revenue_Log'])
            self.firm_network.add_agents([firm_agent], [ag_node])
        a_list_total_firms = ap.AgentList(self, self.firm_network.agents)
        # set the initial firm that is removed
        list_b_is_removed = list(
            map(lambda x: x in self.p.list_init_remove_firm_code,
                self.firm_network.agents.code))
        self.a_list_firms_removed = a_list_total_firms.select(
            list_b_is_removed)
        self.a_list_firms_removed.is_removed = True

    def update(self):
        # Update list of unhappy people
        # self.agents.update_happiness()
        # self.unhappy = self.agents.select(self.agents.happy == False)

        # Stop simulation if reached terminal number of iteration
        if self.t == self.int_n_iter:
            self.stop()

    def step(self):
        # Move unhappy people to new location
        self.unhappy.find_new_home()

    def end(self):
        pass

    def draw_network(self):
        import matplotlib.pyplot as plt
        plt.rcParams['font.sans-serif'] = 'SimHei'
        pos = nx.nx_agraph.graphviz_layout(self.firm_network.graph,
                                           prog="twopi",
                                           args="")
        node_label = nx.get_node_attributes(self.firm_network.graph, 'Name')
        node_size = list(
            nx.get_node_attributes(self.firm_network.graph,
                                   'Revenue_Log').values())
        node_size = list(map(lambda x: x**2, node_size))
        plt.figure(figsize=(12, 12), dpi=300)
        nx.draw(self.firm_network.graph,
                pos,
                node_size=node_size,
                labels=node_label,
                font_size=6)
        plt.savefig("network.png")


model = Model(dct_sample_para)
model.setup()
# model.draw_network()
