import sys
import time
import tkinter as tk
import webbrowser
import pyperclip
import asyncio
import json
from factoryKit import FactoryKit


# count entries in each list
def getIpdQties(orders):
  return {key: max(0, len(value) - FactoryKit.bs1Stock[key]) for key, value in orders.items()}

# toggle the display of order links
def toggleLinks(root, frame, key, entry_counts, expandable_frames):
  if expandable_frames[key].winfo_ismapped():
    expandable_frames[key].grid_remove()
  else:
    expandable_frames[key].grid(row=list(entry_counts.keys()).index(key) + 1, column=0, columnspan=3, sticky='w')
  frame.update_idletasks()
  frame.event_generate("<Configure>")
  root.update_idletasks()
  root.geometry(f"{frame.winfo_width()}x{root.winfo_height()}")

# handle link clicks
def onLinkClick(label, url):
  webbrowser.open(url)  # Open the link in the default browser
  label.config(fg="gray")  # Change the text color after clicking

# copy counts to clipboard [CORE]
def copyCounts(orders, ipdShortageCounts):
  inclRxIpdShortage = ipdShortageCounts["inclRx"]
  hmdOnlyIpdShortage = ipdShortageCounts["hmdOnly"]
  clipboardText = "Total Beyond shortage across all IPDs: {}\n".format(str(sum(hmdOnlyIpdShortage.values())) + " --- INCLUDING LENS ORDERS: " + str(sum(inclRxIpdShortage.values())))
  
  for key in inclRxIpdShortage:
    if inclRxIpdShortage[key] > 0:
      keymm = key[-2:] + "mm"
      clipboardText += "- {}: {}\n".format(keymm, str(hmdOnlyIpdShortage[key]) + " --- w/lenses: " + str(inclRxIpdShortage[key]))
    
  pyperclip.copy(clipboardText)

# copy all details to clipboard [CORE]
def copyDetails(orders, ipdShortageCounts):
  inclRxIpdShortage = ipdShortageCounts["inclRx"]
  hmdOnlyIpdShortage = ipdShortageCounts["hmdOnly"]
  clipboardText = "Total Beyond shortage across all IPDs: {}\n".format(str(sum(hmdOnlyIpdShortage.values())) + " --- INCLUDING LENS ORDERS: " + str(sum(inclRxIpdShortage.values())))
  
  for key in inclRxIpdShortage:
    if inclRxIpdShortage[key] > 0:
      keymm = key[-2:] + "mm"
      clipboardText += "- {}: {}\n".format(keymm, str(hmdOnlyIpdShortage[key]) + " --- w/lenses: " + str(inclRxIpdShortage[key]))
      if orders[key]:  # Check if there are orders to display
        for order in orders[key]:
          bigOrderID = order['bigOrder']['id']
          link = f"https://main-ocean-arda.bigscreencloud.com/shop/order/{bigOrderID}"
          clipboardText += "  - || {} ||\n".format(link)

  pyperclip.copy(clipboardText)

def runReport(root, frame):
  # asyncio.create_task(eventTimer(frame)) #non-functioning
  asyncio.create_task(eventSequence(root, frame))

async def eventTimer(frame): # currently unused
  timer_label = tk.Label(frame, text="00:00:00", font=("Arial", 12, "bold"))
  timer_label.grid(row=0, column=2, padx=10, pady=5)
  # title the label "time elapsed"
  timer_label_title = tk.Label(frame, text="Time Elapsed", font=("Arial", 12, "bold"))
  timer_label_title.grid(row=0, column=0, padx=10, pady=5)
  start_time = time.time()
  while True:
      elapsed_time = time.time() - start_time
      timer_label.config(text=time.strftime("%H:%M:%S", time.gmtime(elapsed_time)))
      frame.update_idletasks()
      await asyncio.sleep(1)

# [CORE]
async def eventSequence(root, frame):
  # Whether command line argument "shippableOnly" is present
  shippableOnly = len(sys.argv) > 1 and sys.argv[1] == "shippableOnly"

  # Initialize the orders dictionary with sample data
  # orders = {
  #     'ipd55': [{'bigOrder': {'id': '12345'}}, {'bigOrder': {'id': '12346'}}],
  #     'ipd56': [{'bigOrder': {'id': '12347'}}],
  #     'ipd57': [{'bigOrder': {'id': '12348'}}, {'bigOrder': {'id': '12349'}}],
  #     'ipd58': [{'bigOrder': {'id': '12350'}}, {'bigOrder': {'id': '12351'}}, {'bigOrder': {'id': '12352'}}],
  #     # 'ipd59': [],
  #     # 'ipd61': [],
  #     # 'ipd62': [],
  #     # 'ipd63': [],
  #     # 'ipd64': [],
  #     # 'ipd65': [],
  #     # 'ipd66': [],
  #     # 'ipd67': [],
  #     # 'ipd68': [],
  #     # 'ipd69': [],
  #     # 'ipd71': [],
  #     # 'ipd72': []
  # } #debug
  # ordersGroup = {
  #   'allOrders': orders['ipd55'],
  #   'orders': orders,
  #   'ordersInclRx': orders
  # } #debug
  # await asyncio.sleep(3) #debug
  FactoryKit.init()
  FactoryKit.loadBs1Stock()
  if shippableOnly:
    ordersGroup = FactoryKit.getShippableOrders()
    root.title("Shippable Orders Report")
  else:
    ordersGroup = FactoryKit.getUnmetDemandOrders()
    root.title("Unmet Demand Report")
  processOrders(root, frame, ordersGroup, shippableOnly)
  root.bell()
  root.deiconify()
  root.focus_force()

# [CORE]
def processOrders(root, frame, ordersGroup, shippableOnly):
  allOrders = ordersGroup["allOrders"]
  orders = ordersGroup["orders"]
  ordersInclRx = ordersGroup["ordersInclRx"]
  hmdOnlyIpdShortage = getIpdQties(orders)
  inclRxIpdShortage = getIpdQties(ordersInclRx)
  ipdShortageCounts = {"inclRx": inclRxIpdShortage, "hmdOnly": hmdOnlyIpdShortage}

  # Set up column header text
  colHeader_counts = "Inventory QTY Short"
  if shippableOnly:
    colHeader_counts = "Shippable Order QTY"
    hmdOnlyIpdShortage["total"] = sum(hmdOnlyIpdShortage.values())
    inclRxIpdShortage["total"] = sum(inclRxIpdShortage.values())

  # Create a grid to display the counts
  headerIpdLabel = tk.Label(frame, text="Beyond IPD", font=("Arial", 12, "bold"))
  headerIpdLabel.grid(row=0, column=0, padx=10, pady=5)
  headerCountsLabel = tk.Label(frame, text=colHeader_counts, font=("Arial", 12, "bold"))
  headerCountsLabel.grid(row=0, column=1, padx=10, pady=5)

  # Create a frame to hold the expandable sections
  linkFrames = {}

  # Display each key and its entry count
  for rowNum, (key, count) in enumerate(inclRxIpdShortage.items(), start=1):
    showLinksBtn = False
    linksOrders = ordersInclRx
    btnText_links = f"Show all {key}"

    # Populate the links if there are entries
    if count > 0:
      countLabelText = str(hmdOnlyIpdShortage[key]) + " --- INCLUDING LENS ORDERS: " + str(count)
      if shippableOnly:
        countLabelText = str(count)
      ipdLabel = tk.Label(frame, text=key, font=("Arial", 10))
      ipdLabel.grid(row=rowNum, column=0, padx=10, pady=5, sticky='w')
      countLabel = tk.Label(frame, text=countLabelText, font=("Arial", 10))
      countLabel.grid(row=rowNum, column=1, padx=10, pady=5, sticky='w')
      
      # Create a button to toggle the display of links
      if shippableOnly == False:
        showLinksBtn = True
      elif rowNum == len(inclRxIpdShortage):
        showLinksBtn = True
        btnText_links = "All links"
        linksOrders[f"{key}"] = allOrders

      if showLinksBtn:
        # Create a frame for the links
        innerFrame = tk.Frame(frame)
        linkFrames[key] = innerFrame

        # Use a default argument to capture the current key value
        linksButton = tk.Button(frame, text=btnText_links, 
                                command=(lambda currentKey=key: 
                                          toggleLinks(root, frame, currentKey, linksOrders, linkFrames)))
        linksButton.grid(row=rowNum, column=2, padx=10, pady=5)

        # add tkinter label to the frame which shows keyName: count
        label = tk.Label(innerFrame, text=f"{key}: {countLabelText}", font=("Arial", 10, "bold"))
        label.pack(anchor="w")  # Use anchor="w" for left-aligned text
        for idx, order in enumerate(linksOrders[key]):
          bigOrderID = order['bigOrder']['id']
          link = f"https://main-ocean-arda.bigscreencloud.com/shop/order/{bigOrderID}"
          link_label = tk.Label(innerFrame, text=f"{idx + 1}. {link}", fg="blue", cursor="hand2")
          link_label.pack(anchor="w")  # Use anchor="w" for left-aligned text
          link_label.bind("<Button-1>", lambda e, lbl=link_label, url=link: onLinkClick(lbl, url))


  # Create buttons to copy the counts and all details to the clipboard
  copyCountsBtn = tk.Button(frame, text="Copy counts", command=lambda: copyCounts(ordersInclRx, ipdShortageCounts))
  copyCountsBtn.grid(row=len(inclRxIpdShortage) + 1, column=0, padx=10, pady=5, sticky='ew')
  copyDetailsBtn = tk.Button(frame, text="Copy counts and BigOrder links", command=lambda: copyDetails(ordersInclRx, ipdShortageCounts))
  copyDetailsBtn.grid(row=len(inclRxIpdShortage) + 1, column=1, padx=10, pady=5, sticky='ew')

  frame.update_idletasks()
  frame.event_generate("<Configure>")
  root.update_idletasks()
  root.geometry(f"{frame.winfo_width()}x{root.winfo_height()}")

  # print ipdShortageCounts as json
  print("FactoryKit.bs1Stock:")
  print(json.dumps(FactoryKit.bs1Stock, indent=4))
  # print("ordersGroup:")
  # print(json.dumps(ordersGroup, indent=4))
  print("ipdShortageCounts:")
  print(json.dumps(ipdShortageCounts, indent=4))


# Start the Tkinter event loop with asyncio
async def main():

  # Create the main tkinter window
  root = tk.Tk()
  root.title("[{}{}".format(time.strftime("%I:%M:%S %p", time.localtime(time.time() + 135)), " ETA] --- NOW LOADING..."))
  root.grid_rowconfigure(0, weight=1)
  root.grid_columnconfigure(0, weight=1)

  # Create a frame to hold the canvas and scrollbar
  container = tk.Frame(root)
  container.grid(row=0, column=0, sticky="nsew")

  # Create the canvas with a vertical scrollbar
  canvas = tk.Canvas(container)
  scrollbar = tk.Scrollbar(container, orient="vertical", command=canvas.yview)
  
  # Configure the scrollbar
  canvas.configure(yscrollcommand=scrollbar.set)

  # Position the scrollbar and canvas
  scrollbar.pack(side="right", fill="y")
  canvas.pack(side="left", fill="both", expand=True)

  # Create a frame inside the canvas
  frame = tk.Frame(canvas)
  frame.grid_columnconfigure(0, minsize=640)
  frame.grid_columnconfigure(2, pad=33)

  # Create a window inside the canvas for the frame
  canvas_frame = canvas.create_window((0, 0), window=frame, anchor="nw")

  # Update the scrollregion whenever the frame changes
  def on_frame_configure(event):
    canvas.configure(scrollregion=canvas.bbox("all"))
    canvas.event_generate("<Configure>")

  # Resize the canvas window when the frame changes
  def on_canvas_configure(event):
    canvas_width = event.width
    canvas.itemconfig(canvas_frame, width=canvas_width)
    canvas.update_idletasks()
    new_width = canvas.winfo_width() + 17
    current_geometry = root.geometry()
    
    # Extract current height from geometry
    current_height = current_geometry.split('x')[1].split('+')[0]
    
    # Set new geometry maintaining current height
    root.geometry(f"{new_width}x{current_height}")
  
  def _on_mousewheel(event):
    canvas.yview_scroll(-1 * int(event.delta/120), "units")

  frame.bind("<Configure>", on_frame_configure)
  canvas.bind("<Configure>", on_canvas_configure)
  canvas.bind_all("<MouseWheel>", _on_mousewheel)

  # Run the report
  runReport(root, frame)

  # Set window size constraints
  root.minsize(width=640, height=240)
  root.maxsize(width=1600, height=960)
  root.geometry("640x960")

  # update_window_width()

  # Run the Tkinter event loop
  while True:
    frame.update()
    root.update()
    await asyncio.sleep(0.01)

# Run the asyncio event loop
if __name__ == "__main__":
  asyncio.run(main())