#!/usr/bin/env Rscript

# Create the compact, committed data products used to render ebswp.qmd.
# Full fitted objects remain local because they are large and are recreated by
# the model and diagnostic scripts documented in Appendix A-1.

project_root <- normalizePath(getwd(), mustWork = TRUE)
output_root <- file.path(project_root, "results", "report_inputs")
dir.create(output_root, recursive = TRUE, showWarnings = FALSE)

copy_required <- function(from, to) {
  if (!file.exists(from)) {
    stop("Missing required report input: ", from)
  }
  dir.create(dirname(to), recursive = TRUE, showWarnings = FALSE)
  if (!file.copy(from, to, overwrite = TRUE, copy.date = TRUE)) {
    stop("Could not copy ", from, " to ", to)
  }
}

copy_tree_files <- function(from_dir, to_dir, pattern) {
  files <- list.files(from_dir, pattern = pattern, full.names = TRUE)
  if (!length(files)) {
    stop("No matching report inputs in ", from_dir)
  }
  for (from in files) {
    copy_required(from, file.path(to_dir, basename(from)))
  }
}

compact_opt <- function(fit) {
  if ("objective" %in% names(fit$opt)) {
    return(fit$opt[c("objective", "max_gradient", "Convergence_check")])
  }
  list(
    opt = fit$opt$opt[c("objective")],
    Convergence_check = fit$opt$Convergence_check,
    max_gradient = fit$opt$max_gradient
  )
}

compact_fit <- function(
  fit,
  quantities = character(),
  data_fields = character(),
  parameter_fields = character()
) {
  list(
    opt = compact_opt(fit),
    convergence = fit$convergence,
    quantities = fit$quantities[quantities],
    data_list = fit$data_list[data_fields],
    estimated_params = fit$estimated_params[parameter_fields]
  )
}

full_fit_file <- file.path(
  project_root,
  "results", "canonical_pm", "ebs_pollock_method_fits.rds"
)
full_retro_file <- file.path(
  project_root,
  "results", "canonical_pm",
  "ebs_pollock_nonparametric_two_stage_retro_9peels.rds"
)
if (!file.exists(full_fit_file) || !file.exists(full_retro_file)) {
  stop(
    "Full canonical fit and retrospective products are required to refresh ",
    "report inputs. See Appendix A-1."
  )
}

fits <- readRDS(full_fit_file)
fit_quantities <- c("ssb", "R", "biomass")
report_fits <- list(
  data = list(fleet_control = fits$data$fleet_control),
  years = fits$years,
  admb_ssb = local({
    lines <- readLines(file.path(fits$admb_directory, "pm.rep"))
    read_vector <- function(key) {
      start <- grep(paste0("^", key, "$"), lines)[1]
      rows <- list()
      i <- start + 1L
      while (i <= length(lines)) {
        values <- suppressWarnings(as.numeric(strsplit(trimws(lines[i]), " +")[[1]]))
        if (anyNA(values) || length(values) < 2L) break
        rows[[length(rows) + 1L]] <- values[1:2]
        i <- i + 1L
      }
      out <- as.data.frame(do.call(rbind, rows))
      names(out) <- c("Year", "Value")
      out
    }
    read_vector("SSB")
  }),
  admb_rec = local({
    lines <- readLines(file.path(fits$admb_directory, "pm.rep"))
    start <- grep("^R$", lines)[1]
    rows <- list()
    i <- start + 1L
    while (i <= length(lines)) {
      values <- suppressWarnings(as.numeric(strsplit(trimws(lines[i]), " +")[[1]]))
      if (anyNA(values) || length(values) < 2L) break
      rows[[length(rows) + 1L]] <- values[1:2]
      i <- i + 1L
    }
    out <- as.data.frame(do.call(rbind, rows))
    names(out) <- c("Year", "Value")
    out
  }),
  admb_biomass = fits$admb_biomass,
  stage_a = compact_fit(fits$stage_a),
  nonparametric_pm = compact_fit(
    fits$nonparametric_pm,
    quantities = c(fit_quantities, "sel_at_age", "comp_obs", "comp_hat"),
    data_fields = c("fleet_control", "comp_data")
  ),
  standard_multinomial = compact_fit(
    fits$standard_multinomial,
    quantities = fit_quantities
  ),
  ar1_penalized = compact_fit(
    fits$ar1_penalized,
    parameter_fields = c("sel_curve_pen", "sel_dev_log_sd")
  ),
  ar1_2d = compact_fit(
    fits$ar1_2d,
    quantities = c(fit_quantities, "sel_at_age"),
    parameter_fields = c("sel_coff_dev", "sel_curve_pen", "sel_dev_log_sd")
  )
)
saveRDS(
  report_fits,
  file.path(output_root, "ebs_pollock_method_fits_report.rds"),
  compress = "xz"
)

retrospective <- readRDS(full_retro_file)
report_retro <- list(
  Rceattle_list = lapply(retrospective$Rceattle_list, function(fit) {
    list(
      opt = list(
        Convergence_check = fit$opt$Convergence_check,
        max_gradient = fit$opt$max_gradient
      ),
      data_list = list(styr = fit$data_list$styr),
      quantities = fit$quantities[c("ssb", "biomass", "R", "F_spp")]
    )
  }),
  peel_details = lapply(retrospective$peel_details, function(x) {
    list(
      terminal_selectivity_max_difference =
        x$terminal_selectivity_max_difference
    )
  }),
  mohns = retrospective$mohns,
  specification = retrospective$specification
)
saveRDS(
  report_retro,
  file.path(output_root, "ebs_pollock_retrospective_report.rds"),
  compress = "xz"
)

root_copies <- c(
  "results/canonical_pm/model_summary.csv" = "model_summary.csv",
  "results/canonical_pm/lineage.csv" = "lineage.csv",
  "results/canonical_pm/ebs_pollock_osa_residuals.rds" = "ebs_pollock_osa_residuals.rds",
  "results/canonical_pm/ebs_pollock_osa_diagnostics.rds" = "ebs_pollock_osa_diagnostics.rds",
  "results/canonical_pm/ebs_pollock_nonparametric_two_stage_retro_9peels_mohns.csv" =
    "ebs_pollock_retrospective_mohns.csv"
)
for (from in names(root_copies)) {
  copy_required(file.path(project_root, from), file.path(output_root, root_copies[[from]]))
}

copy_tree_files(
  file.path(project_root, "results", "canonical_pm", "cohort_revision_diagnostics"),
  file.path(output_root, "cohort_revision_diagnostics"),
  "\\.(csv|png|txt)$"
)
copy_tree_files(
  file.path(project_root, "results", "canonical_pm", "case_deletion_diagnostics"),
  file.path(output_root, "case_deletion_diagnostics"),
  "\\.csv$"
)
copy_tree_files(
  file.path(project_root, "results", "canonical_pm", "bridge_forward_pass"),
  file.path(output_root, "bridge_forward_pass"),
  "(results\\.rds|summary\\.csv|trajectories\\.csv)$"
)
copy_tree_files(
  file.path(project_root, "results", "rceattle_5.8.1_validation", "self_test"),
  file.path(output_root, "rceattle_5.8.1_validation", "self_test"),
  "(validation_summary|index_truncation_diagnostics_by_fleet|self_test_restart_comparison)\\.csv$"
)
copy_tree_files(
  file.path(project_root, "results", "rceattle_5.8.1_validation", "extended_diagnostics"),
  file.path(output_root, "rceattle_5.8.1_validation", "extended_diagnostics"),
  "(jitter_50_summary|profile_M_age3plus_components)\\.csv$"
)

projection_files <- c(
  "manifest.csv", "lineage.csv", "age_schedules.csv",
  "fishery_selectivity_recent_years.csv", "spm_detail.csv",
  "spm_projection_means.csv", "tier3_seven_scenario_table.csv"
)
for (name in projection_files) {
  copy_required(
    file.path(project_root, "results", "canonical_pm", "spmR_projection", name),
    file.path(output_root, "spmR_projection", name)
  )
}
copy_required(
  file.path(
    project_root, "results", "canonical_pm",
    "spmR_projection_alt2_fixed1300", "spm_detail.csv"
  ),
  file.path(output_root, "spmR_projection_alt2_fixed1300", "spm_detail.csv")
)

copy_tree_files(
  file.path(project_root, "results", "dsem_5.8.1_sst"),
  file.path(output_root, "dsem_5.8.1_sst"),
  "\\.(csv|png)$"
)
copy_tree_files(
  file.path(project_root, "results", "dsem_5.8.1_sst_randomized"),
  file.path(output_root, "dsem_5.8.1_sst_randomized"),
  "\\.(csv|png)$"
)
unlink(file.path(
  output_root,
  c(
    "dsem_5.8.1_sst/session_info.txt",
    "dsem_5.8.1_sst_randomized/session_info.txt"
  )
))
copy_required(
  file.path(
    project_root, "results", "rceattle_5.20.0_validation",
    "DRY_RUN_SUMMARY.md"
  ),
  file.path(
    output_root, "rceattle_5.20.0_validation", "DRY_RUN_SUMMARY.md"
  )
)
copy_tree_files(
  file.path(
    project_root, "results", "rceattle_5.20.0_validation"
  ),
  file.path(output_root, "rceattle_5.20.0_validation"),
  paste0(
    "(convergence_summary|fit_comparison_summary|fleet_settings_5\\.20\\.0|",
    "lineage|self_test_summary|projection_input_sha256_comparison|",
    "projection_comparison_summary)\\.csv$"
  )
)

# Release products should retain reproducible relative locations rather than
# analyst-specific absolute paths.
model_lineage_file <- file.path(output_root, "lineage.csv")
model_lineage <- utils::read.csv(model_lineage_file, check.names = FALSE)
model_lineage$Value[model_lineage$Item == "Rceattle source worktree"] <-
  "grantdadams/Rceattle (revision recorded separately)"
model_lineage$Value[model_lineage$Item == "Input workbook"] <-
  "Data/EBS_24_pollock_m23_rceattle_full_1964-2024.xlsx"
model_lineage$Value[model_lineage$Item == "Modified ADMB report"] <-
  "ADMB/m23_rceattle_full/pm.rep (generated locally)"
utils::write.csv(model_lineage, model_lineage_file, row.names = FALSE)

migration_lineage_file <- file.path(
  output_root, "rceattle_5.20.0_validation", "lineage.csv"
)
migration_lineage <- utils::read.csv(
  migration_lineage_file,
  check.names = FALSE
)
migration_lineage$Value[migration_lineage$Item == "Rceattle library"] <-
  ".r-lib-rceattle-5.20.0 (local, not versioned)"
migration_lineage$Value[migration_lineage$Item == "Source workbook"] <-
  "results/rceattle_5.20.0_validation/regenerated_settings_5.20.0.xlsx"
migration_lineage$Value[migration_lineage$Item == "5.8.1 baseline file"] <-
  "results/canonical_pm/ebs_pollock_method_fits.rds"
utils::write.csv(migration_lineage, migration_lineage_file, row.names = FALSE)

projection_lineage_file <- file.path(
  output_root, "spmR_projection", "lineage.csv"
)
projection_lineage <- utils::read.csv(
  projection_lineage_file,
  check.names = FALSE
)
projection_lineage$source_file <-
  "results/canonical_pm/ebs_pollock_method_fits.rds"
utils::write.csv(projection_lineage, projection_lineage_file, row.names = FALSE)

manifest_files <- list.files(output_root, recursive = TRUE, full.names = TRUE)
manifest_files <- manifest_files[file.info(manifest_files)$isdir %in% FALSE]
manifest <- data.frame(
  path = sub(paste0("^", output_root, "/"), "", manifest_files),
  bytes = file.info(manifest_files)$size,
  md5 = unname(tools::md5sum(manifest_files)),
  stringsAsFactors = FALSE
)
utils::write.csv(manifest, file.path(output_root, "manifest.csv"), row.names = FALSE)

message(
  "Prepared ", nrow(manifest), " report inputs (",
  format(sum(manifest$bytes), big.mark = ","), " bytes) under ", output_root
)
