library(shiny)
library(randomForest)
## randomForest 4.7-1.2
## Type rfNews() to see new features/changes/bug fixes.
library(caret)
## Loading required package: ggplot2
## 
## Attaching package: 'ggplot2'
## The following object is masked from 'package:randomForest':
## 
##     margin
## Loading required package: lattice
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following object is masked from 'package:randomForest':
## 
##     combine
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(scales)

# Lookup for project type labels
proj_type_labels <- c(
  "1" = "Wastewater Treatment",
  "2" = "General Industrial",
  "3" = "Tank Ring",
  "4" = "Refinery",
  "5" = "Industrial Process",
  "6" = "Utility",
  "7" = "Civil and Foundation",
  "8" = "Demolition",
  "9" = "Piping Replacement",
  "10" = "Environmental",
  "11" = "Water Treatment",
  "12" = "Pipeline"
)

# Load and preprocess data
df <- read.csv("estimatingoverview3.20.25.csv")
df <- df[df$Status %in% c(1, 2), ]
df$ProjectType <- factor(as.character(df$ProjType), levels = names(proj_type_labels), labels = proj_type_labels)

# Save unscaled OH&P %
df$Percentof_raw <- df$Percentof

# Normalize Percentof for ML
df$Percentof <- scale(df$Percentof)

# Balance dataset
set.seed(42)
wins <- df[df$Status == 1, ]
losses <- df[df$Status == 2, ]
min_n <- min(nrow(wins), nrow(losses))
df_bal <- rbind(wins[sample(nrow(wins), min_n), ], losses[sample(nrow(losses), min_n), ])
df_bal <- df_bal[sample(nrow(df_bal)), ]

# Train model
model_data <- df_bal[, c("Amount", "Percentof", "ProjectType")]
model_data$ProjectType <- factor(model_data$ProjectType)
model_status <- as.factor(df_bal$Status)

rf_model <- randomForest(x = model_data, y = model_status, ntree = 200)
proj_type_levels <- levels(model_data$ProjectType)

# UI
ui <- fluidPage(
  titlePanel("Win Probability Predictor"),
  sidebarLayout(
    sidebarPanel(
      textInput("amount", "Project Amount ($):", value = comma(1000000)),
      
      actionButton("toggle_ohp_mode", "Switch to $ OH&P"),
      
      conditionalPanel(
        condition = "output.ohpMode == 'percent'",
        numericInput("percent", "OH&P %:", value = 10, min = 0, max = 100, step = 0.1)
      ),
      conditionalPanel(
        condition = "output.ohpMode == 'dollar'",
        numericInput("dollar", "OH&P $:", value = 100000, min = 0, step = 1000)
      ),
      
      selectInput("proj_type", "Project Type:", choices = setNames(proj_type_labels, proj_type_labels)),
      actionButton("predict_btn", "Predict")
    ),
    mainPanel(
      verbatimTextOutput("prediction_result"),
      uiOutput("unmeasured_factors_ui")
    )
  )
)

# Server
server <- function(input, output, session) {
  # Track input mode for OH&P: "percent" or "dollar"
  ohp_mode <- reactiveVal("percent")
  
  observeEvent(input$toggle_ohp_mode, {
    if (ohp_mode() == "percent") {
      ohp_mode("dollar")
      updateActionButton(session, "toggle_ohp_mode", label = "Switch to % OH&P")
    } else {
      ohp_mode("percent")
      updateActionButton(session, "toggle_ohp_mode", label = "Switch to $ OH&P")
    }
  })
  
  # Provide reactive OH&P percent synced with dollar
  ohp_percent <- reactive({
    if (ohp_mode() == "percent") {
      input$percent
    } else {
      amount <- as.numeric(gsub(",", "", input$amount))
      if (is.na(amount) || amount == 0) return(0)
      round((input$dollar / amount) * 100, 2)
    }
  })
  
  # Sync dollar input with percent
  observe({
    req(ohp_mode() == "dollar")
    amount <- as.numeric(gsub(",", "", input$amount))
    if (!is.na(amount) && amount > 0 && !is.null(input$dollar)) {
      pct <- round((input$dollar / amount) * 100, 2)
      updateNumericInput(session, "percent", value = pct)
    }
  })
  
  # Expose OH&P mode to JS for conditionalPanel
  output$ohpMode <- reactive({ ohp_mode() })
  outputOptions(output, "ohpMode", suspendWhenHidden = FALSE)

  observeEvent(input$predict_btn, {
    amount_input <- as.numeric(gsub(",", "", input$amount))
    percent_input <- ohp_percent()
    
    new_data <- data.frame(
      Amount = amount_input,
      Percentof = as.numeric(scale(percent_input, center = attr(df$Percentof, "scaled:center"), scale = attr(df$Percentof, "scaled:scale"))),
      ProjectType = factor(input$proj_type, levels = proj_type_levels)
    )

    pred <- predict(rf_model, new_data, type = "prob")
    pred_class <- ifelse(pred[, "1"] > 0.5, "Win", "Loss")

    output$prediction_result <- renderPrint({
      cat("Predicted Class:", pred_class, "\n")
      cat("Probability of Win:", round(pred[, "1"], 4), "\n")
      cat("Probability of Loss:", round(pred[, "2"], 4))
    })

    output$unmeasured_factors_ui <- renderUI({
      tagList(
        tags$hr(),
        h4("Note on Unmeasured Factors"),
        helpText("This model is limited to historical data between 2024 and early 2025, and does not measure the following in predictions:"),
      tags$ul(
        tags$li("Client relationships and prior history"),
        tags$li("Competitor bids and number of competitors"),
        tags$li("When/if an estimate review was conducted"),
        tags$li("How many estimators worked on the estimate"),
        tags$li("If a site walk was conducted"),
        tags$li("Project duration"),
        tags$li("If UCI is a GC or Sub"),
        tags$li("Bid quality or proposal narrative strength")
        )
      )
    })
  })
}

# Run App
shinyApp(ui = ui, server = server)
## PhantomJS not found. You can install it with webshot::install_phantomjs(). If it is installed, please make sure the phantomjs executable can be found via the PATH variable.
## 
## Listening on http://127.0.0.1:4185