from azure.identity import AzureCliCredential
from azure.digitaltwins.core import DigitalTwinsClient

# using yaml instead of 
import yaml
import uuid

# using altair instead of matplotlib for vizuals
import numpy as np
import pandas as pd

# you will get this from the ADT resource at portal.azure.com
your_digital_twin_url = "home-test-twin.api.wcus.digitaltwins.azure.net"

azure_cli = AzureCliCredential()
service_client = DigitalTwinsClient(
    your_digital_twin_url, azure_cli)
service_client

"""
Explanation: Purchasing tickets
An example use case of the Digital Twin
This example just includes:
* Connecting
* Building relationships between models
* Updating values
* Modifying the relationships and updating twins
* Querying twins by relationship
In the previous scenario we made a bunch of customers. In this scenario we built a bunch of individual users 
This is the SDK repo on Github
Here is the doc on the query language
End of explanation
"""


patron = service_client.get_model("dtmi:mymodels:patron;1")
patron.as_dict()

"""
Explanation: So from the previous notebook we uploaded some models.
End of explanation
"""


query_expression = "SELECT * FROM digitaltwins t where IS_OF_MODEL('dtmi:billmanh:patron;1')"
query_result = service_client.query_twins(query_expression)


"""
Explanation: And we have several customers in our ecosystem that are made from the patron model.
End of explanation
"""


values = []
for i in query_result:
    values.append(i)

df_customers = pd.DataFrame([[i['$dtId'],i['satisfaction']] for i in values],
                           columns=['id','satisfaction'])

df_customers

"""
Explanation: Note the query object loves to drop values. To keep from making multiple queries, save the data somewhere.
End of explanation
"""


# service_client.delete_model(ticket_model_id)

# ticket_model_json = yaml.safe_load(open("models/ticket.json"))
# service_client.create_models([ticket_model_json])

ticket_model_id = "dtmi:mymodels:ticket;1"
get_model = service_client.get_model(ticket_model_id)
get_model.as_dict()

"""
Explanation: Let's add another model for tickets.  In our very simple model, we will assume that customers will buy tickets to our events.
End of explanation
"""


def generate_twin(name,model_id):
    digital_twin_id = f'{name}-{str(uuid.uuid4())}'
    dt_json = {
        "$metadata": {
            "$model": model_id
        }
    }
    return digital_twin_id,dt_json

def updsert_twin():
    created_twin = service_client.upsert_digital_twin(digital_twin_id, dt_json)
    return created_twin

ticket_id, ticket_json = generate_twin("ticket",ticket_model_id)

ticket_json

"""
Explanation: Note that the dict returned from the service doesn't contain all of the values that you created, but that's ok.
End of explanation
"""


def generate_tickets(title,n_tickets):
    tickets = []
    for i in range(n_tickets):
        ticket_id, ticket_json = generate_twin("ticket",ticket_model_id)
        ticket_json['event_title'] = title
        ticket_json['state'] = 'open'
        ticket_json['ticket_location'] = i
        ticket_json['uid'] = f'ticket-{str(uuid.uuid4())}'
        tickets.append(ticket_json)
    return tickets

tickets_df = pd.concat([pd.DataFrame(generate_tickets('Nirvana',5)),
          pd.DataFrame(generate_tickets('Smashing Pumpkins',5)),
           pd.DataFrame(generate_tickets('Foo Fighters',5))]).reset_index(drop=True)

tickets_df

"""
Explanation: So when I create the tickets I’m going to create them as unsold (available) and for a range of events. I'm just going to introduce some tickets into the system for some shows coming up. These tickets just exist locally at this point, but we will 'go live' when we push them into the ecosystem.
End of explanation
"""


ticket_twins = []
for i in tickets_df.index:
    ticket_data = tickets_df.loc[i]
    ticket_twin_id = ticket_data['uid']
    ticket_json = ticket_data.drop('uid').to_dict()
    created_twin = service_client.upsert_digital_twin(ticket_twin_id, ticket_json)
    ticket_twins.append(created_twin)

ticket_twins[:3]

# # Delete twins if you want
# for i in tickets_df.index:
#     ticket_data = tickets_df.loc[i]
#     ticket_twin_id = ticket_data['uid']
#     service_client.delete_digital_twin(ticket_twin_id)

"""
Explanation: Ok now that we know what tickets we want to sell, let's push them to the digital twin ecosystem. This is exactly the same as what we did with Customers in step one.
End of explanation
"""


# selection from a dropdown menu or something
selection_show = 'Nirvana'
# just grabbing the first customer. 
user = df_customers.loc[0]['id']
user_lat = np.random.randint(0,100)
user_long = np.random.randint(0,100)

query_expression = f"""
SELECT * FROM digitaltwins where IS_OF_MODEL('{ticket_model_id}') 
and state = 'open'
and event_title = '{selection_show}'
"""
query_result = service_client.query_twins(query_expression)

"""
Explanation: So now I have customers and I have tickets, but there is no relationship between them. Let's pretend that I have a website that sells tickets. When a customer buys a ticket on the website, a relationship between the two is established AND the status of the ticket changes to sold. 

website (or mobile app) will tell the user which tickets are still available, and have features that allow them to search by row, or by concert.
the website (or mobile app) gets the latitude and longitude of the user when they buy the tickets. 
once purchased, the status of that ticket changes to sold.

The user experience (mobile app)
Here is where the user will buy tickets. 
This cell is a customer experience. The customer enters the website and looks up the available tickets for the show they want. This data comes from the website.
End of explanation
"""


available_tickets_df = pd.DataFrame([[i['$dtId'],i['event_title'],i['state']] for i in query_result],
                                   columns = ['$dtId','event_title','state'])
available_tickets_df

"""
Explanation: Note that the client dumps records after the query. Try running the cell below a few times. If you need to use the values again and again you need to put them into memory somewhere.
End of explanation
"""


customer_selection = available_tickets_df.loc[0]['$dtId']

service_client.get_digital_twin(customer_selection)

customer_selection = available_tickets_df.loc[0]['$dtId']

patch = [
    {
        "op": "replace",
        "path": "",
        "value": "sold"
    }
]
service_client.update_component(customer_selection,"state", patch)

"""
Explanation: Now the user chooses to buy a ticket. The ticket state changes to closed.
End of explanation
"""


query_expression = f"""
SELECT * FROM digitaltwins where IS_OF_MODEL('{ticket_model_id}') 
and event_title = '{selection_show}'
"""
query_result = service_client.query_twins(query_expression)
available_tickets_df = pd.DataFrame([[i['$dtId'],i['event_title'],i['state']] for i in query_result],
                                   columns = ['$dtId','event_title','state'])
available_tickets_df

"""
Explanation: NOTE that the path is "" when the item is at the root of the state.
End of explanation
"""


print(customer_selection)
print(user)

tickethoder_relationship = {
        "$relationshipId": f"{customer_selection}ownedBy{user}",
        "$sourceId": customer_selection,
        "$relationshipName": "ownedBy",
        "$targetId": user,
        "bought_online": True
    }

service_client.upsert_relationship(
        tickethoder_relationship["$sourceId"],
        tickethoder_relationship["$relationshipId"],
        tickethoder_relationship
    )

[i for i in service_client.list_relationships(customer_selection)]
i

"""
Explanation: Relationships: 
* the target is the leaf (or the customer)
* the source is the branch (or the ticket)
you can add extra stuff to the relationship.
End of explanation
"""


query_expression = f"""
SELECT * FROM digitaltwins t where IS_OF_MODEL('{ticket_model_id}') 
and t.state = 'open'
"""
query_result = service_client.query_twins(query_expression)
available_tickets_df = pd.DataFrame([[i['$dtId'],i['event_title'],i['state']] for i in query_result],
                                   columns = ['$dtId','event_title','state'])
available_tickets_df

other_user = df_customers.loc[len(df_customers)-1]['id']
other_user

tickets_bought = available_tickets_df.loc[:5,'$dtId'].tolist()
tickets_bought

tickets_bought

for t in tickets_bought:
    print(t)
    tickethoder_relationship = {
            "$relationshipId": f"{customer_selection}ownedBy{other_user}",
            "$sourceId": t,
            "$relationshipName": "ownedBy",
            "$targetId": other_user,
            "bought_online": False
        }

    service_client.upsert_relationship(
            tickethoder_relationship["$sourceId"],
            tickethoder_relationship["$relationshipId"],
            tickethoder_relationship
        )
    customer_selection = available_tickets_df.loc[0]['$dtId']

    patch = [
        {
            "op": "replace",
            "path": "",
            "value": "sold"
        }
    ]
    service_client.update_component(t,"state", patch)

"""
Explanation: Another customer will buy several tickets.
End of explanation
"""


query_expression = f"""
SELECT * FROM digitaltwins t where IS_OF_MODEL('{ticket_model_id}') 
"""
query_result = service_client.query_twins(query_expression)
available_tickets_df = pd.DataFrame([[i['$dtId'],i['event_title'],i['state']] for i in query_result],
                                   columns = ['$dtId','event_title','state'])
available_tickets_df

"""
Explanation: Now let's lets look at the tickets.
End of explanation
"""


