# from CONDUIT_FORGE import src.
# from ..src import components
# from ..src import current
# from ..src import mosaic

from .decorators import timer
from conduitforge.components import Component, Port, Pipe, T_connector

from conduitforge.mosaic import Mosaic
from conduitforge.current import Current

import thermo

import networkx as nx
import matplotlib
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from typing import Optional
from pprint import pprint

from colorama import Fore, Back, Style

"""
This file will be used to test all the mosaic features, including the path finding, and maybe the solver.
"""


def cycle_detection() -> None:
    pure_water = thermo.Chemical("water", T=303, P=300_000)  # temperature in Kelvin
    current_a = Current(composition=pure_water)

    """
    The mosaic created should be like this image:

    c_a > t1 > p4 > t2 > corrente de saida
          |         |
          p1 > p2 > p3

    p1 e p3 são verticais, mas eu não consegui deixar eles assim no diagrama

    Esse mosaico tem um loop, e eu quero ver se o algoritmo do mossaico consegue identificar isso

    Se for necessario, eu posso adicionar um tubo entre c_a e t1, e outro tubo entre t2 e a corrente de saida
    """

    p1 = Pipe(internal_diam=3, length=4, orientation=-90)
    p2 = Pipe(internal_diam=3, length=7, orientation=0)
    p3 = Pipe(internal_diam=3, length=4, orientation=90)
    p4 = Pipe(internal_diam=3, length=7, orientation=0)

    t1 = T_connector([3, 3, 3])
    t2 = T_connector([3, 3, 3])

    print(f"\n{current_a.id = }")

    print(f"\n{p1 = }")
    print(f"\n{p1.component_ports[0].id = }")
    print(f"\n{p1.component_ports[1].id = }")

    print(f"\n\n{p2 = }")
    print(f"\n{p2.component_ports[0].id = }")
    print(f"\n{p2.component_ports[1].id = }")

    print(f"\n\n{p3 = }")
    print(f"\n{p3.component_ports[0].id = }")
    print(f"\n{p3.component_ports[1].id = }")

    print(f"\n\n{p4 = }")
    print(f"\n{p4.component_ports[0].id = }")
    print(f"\n{p4.component_ports[1].id = }")

    print(f"\n\n{t1 = }")
    print(f"\n{t1.component_ports[0].id = }")
    print(f"\n{t1.component_ports[1].id = }")
    print(f"\n{t1.component_ports[2].id = }")

    print(f"\n\n{t2 = }")
    print(f"\n{t2.component_ports[0].id = }")
    print(f"\n{t2.component_ports[1].id = }")
    print(f"\n{t2.component_ports[2].id = }")

    m1: Optional[Mosaic] = current_a + t1

    print(f"\n\n\n{m1.id = }\n\n\n")

    m1 = t1 + p1
    m1 = p1 + p2
    m1 = p2 + p3

    m1 = t1 + p4

    m1 = p4 + t2

    m1 = t2 + p3

    m1.create_graph()
    m1.cycle_and_branch_identifier()
    m1.cycle_analyzer()
    # print(f'\n{m1.simple_cycles_as_nodes = }')
    # print(f'\n{m1.unsorted_branches = }')

    # print(f'\n\n{m1.graph.nodes = }')
    # print(f'\n\n{m1.graph.edges = }')

    print(f"\n{m1.joints_list = }")

    # print(f'\n')
    # for port in Port.port_objects:
    #     print(f'\n{port.location = }')
    #     print(f'\n{port.id = }')
    #     print(f'\n{port.spacial_position.coords = }')

    # print(f'\n')
    # for comp in Component.component_objects:
    #     print(f'\n{comp.id = }')
    #     print(f'\n{comp.location = }')
    #     print(f'\n{comp.component_ports[0].id = }')
    #     print(f'\n{comp.component_ports[0].spacial_position = }')

    #     if comp.component_ports[0] not in comp.component_ports:
    #         print(f'\n\n\n\n{comp.component_ports[0].id = }\n\n\n\n')

    # print(f'\n')
    # for node in m1.graph.nodes():
    #     print(f'\n{node = }')

    # if any(node != port.id for port in Port.port_objects for node in m1.graph.nodes()) == False:

    plt.figure(figsize=(8, 6))

    # Desenha o grafo com o layout escolhido, mostrando as conexões (arestas)
    nx.draw(
        m1.graph,
        pos=nx.spring_layout(m1.graph),
        with_labels=True,
        node_color="lightblue",
        node_size=500,
        font_size=7,
        font_color="black",
        edge_color="gray",
        arrows=True,
    )

    # Mostra o gráfico
    plt.show()

    pos = nx.get_node_attributes(m1.graph, "pos")

    # Criando a figura 3D
    fig: plt.Figure = plt.figure()
    ax = fig.add_subplot(111, projection="3d")

    # Plotando os nós
    for node, (x, y, z) in pos.items():
        ax.scatter(x, y, z, s=100)  # s define o tamanho do nó
        ax.text(x, y, z, f"{node}", size=12, zorder=1)

    # Plotando as arestas
    for edge in m1.graph.edges():
        x_values = [pos[edge[0]][0], pos[edge[1]][0]]
        y_values = [pos[edge[0]][1], pos[edge[1]][1]]
        z_values = [pos[edge[0]][2], pos[edge[1]][2]]
        ax.plot(x_values, y_values, z_values, color="black")

    # Configurações de visualização
    ax.set_xlabel("X")
    ax.set_ylabel("Y")
    ax.set_zlabel("Z")

    plt.show()

    """
    # Extrai as coordenadas (x, y, z) para os nós
    x_vals = [pos[node][0] for node in m1.graph.nodes()]
    y_vals = [pos[node][1] for node in m1.graph.nodes()]
    z_vals = [pos[node][2] for node in m1.graph.nodes()]

    # Cria a figura em 3D
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')

    # Plota as arestas
    for edge in m1.graph.edges():
        x_edge = [pos[edge[0]][0], pos[edge[1]][0]]
        y_edge = [pos[edge[0]][1], pos[edge[1]][1]]
        z_edge = [pos[edge[0]][2], pos[edge[1]][2]]
        
        ax.plot(x_edge, y_edge, z_edge, color='black')

    # Plota os nós
    ax.scatter(x_vals, y_vals, z_vals, c='r', s=100)

    # Ajustes de visualização
    ax.set_xlabel('X')
    ax.set_ylabel('Y')
    ax.set_zlabel('Z')

    plt.show()
    """


def equation_gathering_test() -> None:
    """
    Esse teste vai servir pra verificar se as equações estão sendo retornadas ao mosaico de forma
    correta, e na quantidade correta.

    Eu vou criar um sistema de tubos comum, e acionar o método do mosaico para coletar as equações

    Depois de desenhar um sistema, eu preciso anotar manualmente as equações de massa e energia em
    cada um dos componentes, em um papel.

    Depois de ter as equações anotadas, eu vou rodar esse teste e conferir se todas as equações que
    eu tenho no papel foram dadas pelo código.
    """

    """
    The mosaic created should be like this image:

    c_a > t1 > p4 > t2 > corrente de saida
          |         |
          p1 > p2 > p3

    p1 e p3 são verticais, mas eu não consegui deixar eles assim no diagrama

    Esse mosaico tem um loop, e eu quero ver se o algoritmo do mossaico consegue identificar isso

    Se for necessario, eu posso adicionar um tubo entre c_a e t1, e outro tubo entre t2 e a corrente de saida
    """

    pure_water = thermo.Chemical("water", T=303)  # temperature in Kelvin
    current_a = Current(composition=pure_water, P=300_000)

    p1 = Pipe(internal_diam=3, length=4, orientation=-90)
    p2 = Pipe(internal_diam=3, length=7, orientation=0)
    p3 = Pipe(internal_diam=3, length=4, orientation=90)
    p4 = Pipe(internal_diam=3, length=7, orientation=0)

    t1 = T_connector(internal_diam=3)
    t2 = T_connector(internal_diam=3)

    print(f"\n{current_a.id = }")

    print(f"\n{p1 = }")
    print(f"\n{p1.component_ports[0].id = }")
    print(f"\n{p1.component_ports[1].id = }")

    print(f"\n\n{p2 = }")
    print(f"\n{p2.component_ports[0].id = }")
    print(f"\n{p2.component_ports[1].id = }")

    print(f"\n\n{p3 = }")
    print(f"\n{p3.component_ports[0].id = }")
    print(f"\n{p3.component_ports[1].id = }")

    print(f"\n\n{p4 = }")
    print(f"\n{p4.component_ports[0].id = }")
    print(f"\n{p4.component_ports[1].id = }")

    print(f"\n\n{t1 = }")
    print(f"\n{t1.component_ports[0].id = }")
    print(f"\n{t1.component_ports[1].id = }")
    print(f"\n{t1.component_ports[2].id = }")

    print(f"\n\n{t2 = }")
    print(f"\n{t2.component_ports[0].id = }")
    print(f"\n{t2.component_ports[1].id = }")
    print(f"\n{t2.component_ports[2].id = }")

    m1: Optional[Mosaic] = t1 + current_a

    m1 = t1 + p1
    m1 = p1 + p2
    m1 = p2 + p3

    m1 = t1 + p4

    m1 = p4 + t2

    m1 = t2 + p3

    assert m1 is not None
    assert m1.graph is not None

    print(f"\n\n\n{m1.id = }\n\n\n")
    m1.create_graph()
    m1.cycle_and_branch_identifier()
    m1.cycle_analyzer()

    # print(f'\n\n{m1.simple_cycles_as_nodes = }')
    # print(f'\n\n{m1.simple_cycles_as_edges = }')
    # print(f'\n\n{m1.final_directed_cycles = }')

    pprint(f"\n\n{m1.joints_list = }")

    m1.first_mass_flow_guess()

    m1.equation_gathering_from_graph()

    pprint(f"\n{len(m1.all_eq_variables) = }")

    print("\nThis is the number of degrees of freedom of the equations system\n")

    pprint(f"\n{len(m1.all_eq_variables) - len(m1.equation_list)= }\n")

    pprint(f"\n{m1.equation_list = }")

    pprint(f"\n{len(m1.equation_list) = }")

    plt.figure(1, figsize=(8, 6))

    pos1 = nx.spring_layout(m1.graph, seed=12)

    #! This part is used to solve the bugg of the matplot lib not plotting the graphs
    print(f"\n{matplotlib.matplotlib_fname() = }\n")

    print(f"{matplotlib.get_backend() = }")

    # Desenha o grafo com o layout escolhido, mostrando as conexões (arestas)
    nx.draw(
        m1.graph,
        pos1,
        with_labels=True,
        node_color="lightblue",
        node_size=500,
        font_size=7,
        font_color="black",
        edge_color="gray",
        arrows=True,
        connectionstyle="arc3,rad=0.2",
    )

    # Mostra o gráfico
    plt.show()

    plt.figure(2, figsize=(8, 6))

    pos2 = nx.spring_layout(m1.acyclic_graph, seed=12)

    # Desenha o grafo com o layout escolhido, mostrando as conexões (arestas)
    nx.draw(
        m1.acyclic_graph,
        pos2,
        with_labels=True,
        node_color="lightblue",
        node_size=500,
        font_size=7,
        font_color="black",
        edge_color="gray",
        arrows=True,
        connectionstyle="arc3,rad=0.2",
    )

    # Mostra o gráfico
    plt.show()


@timer
def main():
    equation_gathering_test()


main()
