library(shiny)
library(reticulate)
library(reactable)
library(networkD3)
library(bslib)

#use_virtualenv("blockchain-env")
#source_python("blockchain_engine.py")

# HELPER: Find the python file
# If installed as a package, it looks in the library.
# If running locally, it looks in the 'inst/python' folder.
get_py_path <- function() {
  pkg_path <- system.file("python", "blockchain_engine.py", package = "ShinyBlock")
  if (pkg_path == "") {
    # Fallback for local development
    return("../../inst/python/blockchain_engine.py")
  }
  return(pkg_path)
}

# Source the engine
tryCatch({
  source_python(get_py_path())
}, error = function(e) {
  stop("Engine Not Found. Ensure blockchain_engine.py is in inst/python/ and you are running from the package root.")
})

ui <- fluidPage(
  theme = bs_theme(bootswatch = "darkly"),
  titlePanel("Multi-Protocol Blockchain Simulator"),

  sidebarLayout(
    sidebarPanel(
      h4("Node Controller"),
      actionButton("gen_wallet", "1. Generate Identity", class = "btn-info w-100"),
      hr(),
      uiOutput("wallet_display"),
      hr(),
      # MIN = 1 prevents negative difficulty
      numericInput("diff", "Mining Difficulty:", value = 2, min = 1, max = 5),
      actionButton("mine_block", "2. Mine Block", class = "btn-success w-100"),
      hr(),
      downloadButton("download_ledger", "Export Ledger (CSV)", class = "btn-primary w-100")
    ),

    mainPanel(
      tabsetPanel(
        tabPanel("Blockchain Ledger", br(), reactableOutput("chain_ui")),
        tabPanel("Supply Chain", br(),
                 fluidRow(
                   column(4,
                          textInput("prod_id", "Product ID:", "LOT-992"),
                          textInput("loc", "Current Location:", "Warehouse A"),
                          selectInput("status", "Status:", c("Manufactured", "In Transit", "Delivered")),
                          actionButton("update_supply", "Log Movement", class = "btn-warning w-100")),
                   column(8, reactableOutput("supply_table"))
                 )),
        tabPanel("Enterprise", br(),
                 h4("Cross-Border Settlement via Smart Contract"),
                 # MIN = 0 prevents negative payments
                 numericInput("pay_amt", "Amount to Settle:", value = 100, min = 0),
                 actionButton("pay_btn", "Execute Settlement", class = "btn-danger"),
                 verbatimTextOutput("pay_status"),
                 hr(),
                 h4("Hyperledger Fabric Access Control"),
                 textInput("peer_name", "Peer ID:", "Org1_Peer"),
                 actionButton("check_access", "Verify Membership")),
        tabPanel("Network Map", br(),
                 div(style = "display: flex; justify-content: center;",
                     forceNetworkOutput("p2p_plot", height = "500px")))
      )
    )
  )
)

server <- function(input, output, session) {
  bc <- BlockchainEngine(); vm <- SmartContractVM(); supply <- SupplyChainManager(); fabric <- PermissionedNetwork()
  chain_trigger <- reactiveVal(0)
  wallet_keys <- reactiveVal(NULL)

  observeEvent(input$gen_wallet, { wallet_keys(create_wallet()) })

  output$wallet_display <- renderUI({
    keys <- wallet_keys()
    if (is.null(keys)) return(helpText("No identity generated yet."))
    tagList(
      tags$label("Private Key:"),
      tags$p(span(style="color:#00bc8c; cursor:help; font-family:monospace;", title=keys[[1]],
                  paste0(substr(keys[[1]], 1, 15), "... (Hover for full)"))),
      tags$label("Public Key (Address):"),
      tags$p(span(style="color:#3498db; cursor:help; font-family:monospace;", title=keys[[2]],
                  paste0(substr(keys[[2]], 1, 15), "... (Hover for full)")))
    )
  })

  output$chain_ui <- renderReactable({
    chain_trigger()
    raw_chain <- py_to_r(bc$chain)
    df <- do.call(rbind, lapply(raw_chain, function(x) {
      data.frame(Index = x$index, Timestamp = as.character(as.POSIXct(x$timestamp, origin="1970-01-01")),
                 Nonce = x$nonce, Prev_Hash = substr(x$previous_hash, 1, 15), Merkle_Root = x$merkle_root, stringsAsFactors = FALSE)
    }))
    reactable(df, striped = TRUE, bordered = TRUE, theme = reactableTheme(backgroundColor = "#222", color = "#fff"))
  })

  observeEvent(input$mine_block, {
    # Ensure difficulty isn't sabotaged manually
    req(input$diff >= 1)
    last_block <- bc$chain[[length(bc$chain)]]
    new_nonce <- bc$proof_of_work(last_block$nonce, as.integer(input$diff))
    bc$create_block(new_nonce, bc$hash_data(last_block), list(input$tx_data))
    chain_trigger(chain_trigger() + 1)
  })

  output$p2p_plot <- renderForceNetwork({
    chain_trigger()
    n <- length(py_to_r(bc$chain))
    nodes <- data.frame(name = paste0("B", 0:(n-1)), group = 1)
    if (n >= 2) {
      links <- data.frame(source = as.integer(1:(n-1)), target = as.integer(0:(n-2)), value = 1)
      forceNetwork(Links = links, Nodes = nodes, Source = "source", Target = "target",
                   NodeID = "name", Group = "group", opacity = 1, zoom = TRUE,
                   fontSize = 20, linkDistance = 100, charge = -200, bounded = TRUE)
    } else {
      dummy_links <- data.frame(source = as.integer(0), target = as.integer(0), value = 0)
      forceNetwork(Links = dummy_links, Nodes = nodes, Source = "source", Target = "target",
                   NodeID = "name", Group = "group", opacity = 0, zoom = TRUE)
    }
  })

  output$download_ledger <- downloadHandler(
    filename = function() { paste0("ledger-", Sys.Date(), ".csv") },
    content = function(file) {
      raw <- py_to_r(bc$chain)
      df <- do.call(rbind, lapply(raw, function(x) data.frame(Index=x$index, Nonce=x$nonce, Prev.Hash=x$previous_hash, Merkle=x$merkle_root)))
      write.csv(df, file, row.names = FALSE)
    }
  )

  supply_trigger <- reactiveVal(0)
  observeEvent(input$update_supply, {
    supply$track_movement(input$prod_id, input$loc, input$status)
    supply_trigger(supply_trigger() + 1)
  })

  output$supply_table <- renderReactable({
    supply_trigger(); hist <- py_to_r(supply$products[[input$prod_id]])
    if(is.null(hist)) return(reactable(data.frame(Log = "No Data")))
    reactable(do.call(rbind, lapply(hist, as.data.frame)), theme = reactableTheme(backgroundColor = "#222"))
  })

  observeEvent(input$pay_btn, {
    # Server-side validation for negative payments
    req(input$pay_amt >= 0)
    result <- vm$execute_cross_border_payment("Co_A", "Co_B", input$pay_amt)
    output$pay_status <- renderText({ result[[2]] })
  })

  observeEvent(input$check_access, {
    is_v <- fabric$validate_access(input$peer_name)
    showNotification(ifelse(is_v, "Authorized", "Denied"), type = ifelse(is_v, "message", "error"))
  })
}

shinyApp(ui, server)
