# MOSAIC_CREATION_PER_FIELD_UTILS.R
# ==================================
# Utility functions for creating per-field weekly mosaics from per-field daily TIFFs.
# 
# This module aggregates daily per-field 5-band TIFFs (R,G,B,NIR,CI from Script 20)
# into weekly per-field mosaics using MAX compositing.
#
# DATA FLOW:
#   Script 20: field_tiles_CI/{FIELD}/{DATE}.tif (5-band daily, per-field)
#                              ↓
#   Script 40 NEW (this module):
#     For each field:
#       For each week:
#         - Find all daily TIFFs for that week
#         - Stack & create MAX composite
#         - Save: weekly_mosaic/{FIELD}/week_WW_YYYY.tif
#                              ↓
#   Scripts 90/91: Read weekly_mosaic/{FIELD}/week_WW_YYYY.tif (unchanged interface)

#' Generate date range for processing (ISO week-based)
#'
#' @param end_date The end date (Date object or YYYY-MM-DD string)
#' @param offset Number of days to look back from end_date (typically 7 for one week)
#' @return List with week, year, start_date, end_date, days_filter (vector of YYYY-MM-DD strings)
#'
date_list <- function(end_date, offset) {
  if (!lubridate::is.Date(end_date)) {
    end_date <- as.Date(end_date)
    if (is.na(end_date)) {
      stop("Invalid end_date. Expected Date object or YYYY-MM-DD string.")
    }
  }
  
  offset <- as.numeric(offset)
  if (is.na(offset) || offset < 1) {
    stop("Invalid offset. Expected positive number.")
  }
  
  offset <- offset - 1  # Adjust to include end_date
  start_date <- end_date - lubridate::days(offset)
  
  week <- lubridate::isoweek(end_date)
  year <- lubridate::isoyear(end_date)
  
  # Validate: Check that all dates in range belong to same ISO week
  start_week <- lubridate::isoweek(start_date)
  start_year <- lubridate::isoyear(start_date)
  
  if (start_week != week || start_year != year) {
    safe_log(sprintf("WARNING: Date range spans multiple ISO weeks! Start: week %d/%d, End: week %d/%d. Using END date week %d/%d.", 
                     start_week, start_year, week, year, week, year), "WARNING")
  }
  
  days_filter <- seq(from = start_date, to = end_date, by = "day")
  days_filter <- format(days_filter, "%Y-%m-%d")
  
  safe_log(paste("Date range:", start_date, "to", end_date, 
                 "(week", week, "of", year, ")"))
  
  return(list(
    week = week,
    year = year,
    start_date = start_date,
    end_date = end_date,
    days_filter = days_filter
  ))
}

#' Find all per-field daily TIFFs for a specific week
#'
#' @param field_tiles_ci_dir Base directory containing per-field TIFFs 
#'                            (e.g., field_tiles_CI/)
#' @param days_filter Vector of YYYY-MM-DD dates to match
#' @return List with field names and their matching TIFF files for the week
#'
find_per_field_tiffs_for_week <- function(field_tiles_ci_dir, days_filter) {
  
  if (!dir.exists(field_tiles_ci_dir)) {
    safe_log(paste("Field TIFFs directory not found:", field_tiles_ci_dir), "WARNING")
    return(list())
  }
  
  # List all field subdirectories
  field_dirs <- list.dirs(field_tiles_ci_dir, full.names = FALSE, recursive = FALSE)
  
  if (length(field_dirs) == 0) {
    safe_log("No field subdirectories found in field_tiles_CI/", "WARNING")
    return(list())
  }
  
  # For each field, find TIFF files matching the week's dates
  field_tiffs <- list()
  
  for (field in field_dirs) {
    field_path <- file.path(field_tiles_ci_dir, field)
    
    # Find all TIFF files in this field directory
    tiff_files <- list.files(field_path, pattern = "\\.tif$", full.names = TRUE)
    
    if (length(tiff_files) == 0) {
      next  # Skip fields with no TIFFs
    }
    
    # Filter to only those matching week's dates
    matching_files <- tiff_files[grepl(paste(days_filter, collapse = "|"), tiff_files)]
    
    if (length(matching_files) > 0) {
      field_tiffs[[field]] <- sort(matching_files)
    }
  }
  
  safe_log(paste("Found TIFFs for", length(field_tiffs), "fields in week"))
  
  return(field_tiffs)
}

#' Create weekly MAX composite for a single field
#'
#' Loads all daily TIFFs for a field+week combination and creates a MAX composite
#' (per-band maximum across all days).
#'
#' @param tiff_files Vector of TIFF file paths for this field+week
#' @param field_name Name of the field (for logging)
#' @return SpatRaster with 5 bands (R,G,B,NIR,CI), or NULL if fails
#'
create_field_weekly_composite <- function(tiff_files, field_name) {
  
  if (length(tiff_files) == 0) {
    return(NULL)
  }
  
  tryCatch({
    # Load all TIFFs
    rasters <- list()
    for (file in tiff_files) {
      tryCatch({
        r <- terra::rast(file)
        rasters[[length(rasters) + 1]] <- r
      }, error = function(e) {
        # Silently skip load errors (they're already counted)
      })
    }
    
    if (length(rasters) == 0) {
      return(NULL)
    }
    
    # Create MAX composite
    if (length(rasters) == 1) {
      composite <- rasters[[1]]
    } else {
      # Stack all rasters and apply MAX per pixel per band
      collection <- terra::sprc(rasters)
      composite <- terra::mosaic(collection, fun = "max")
    }
    
    # Ensure 5 bands with expected names
    if (terra::nlyr(composite) >= 5) {
      composite <- terra::subset(composite, 1:5)
      names(composite) <- c("Red", "Green", "Blue", "NIR", "CI")
    }
    
    
    return(composite)
    
  }, error = function(e) {
    safe_log(paste("Error creating composite for field", field_name, ":", e$message), "ERROR")
    return(NULL)
  })
}

#' Save per-field weekly mosaic
#'
#' @param raster SpatRaster to save
#' @param output_dir Base output directory (e.g., laravel_app/storage/app/{project}/weekly_mosaic/)
#' @param field_name Name of the field
#' @param week Week number (ISO week)
#' @param year Year (ISO year)
#' @return File path of saved TIFF, or NULL if fails
#'
save_field_weekly_mosaic <- function(raster, output_dir, field_name, week, year) {
  
  if (is.null(raster)) {
    return(NULL)
  }
  
  tryCatch({
    # Create field-specific output directory
    field_output_dir <- file.path(output_dir, field_name)
    dir.create(field_output_dir, recursive = TRUE, showWarnings = FALSE)
    
    # Generate filename: week_WW_YYYY.tif
    filename <- sprintf("week_%02d_%04d.tif", week, year)
    file_path <- file.path(field_output_dir, filename)
    
    # Save raster (silently)
    terra::writeRaster(raster, file_path, overwrite = TRUE)
    
    return(file_path)
    
  }, error = function(e) {
    safe_log(paste("Error saving mosaic for field", field_name, ":", e$message), "ERROR")
    return(NULL)
  })
}

#' Create all weekly mosaics for all fields in a week
#'
#' Main orchestration function. Loops over all fields and creates weekly mosaics.
#'
#' @param dates List from date_list() - contains week, year, days_filter
#' @param field_tiles_ci_dir Input: field_tiles_CI/ directory
#' @param output_dir Output: weekly_mosaic/ directory
#' @return Vector of successfully created file paths
#'
create_all_field_weekly_mosaics <- function(dates, field_tiles_ci_dir, output_dir) {
  
  safe_log(paste("=== Creating Per-Field Weekly Mosaics ==="))
  safe_log(paste("Week:", dates$week, "Year:", dates$year))
  
  # Find all per-field TIFFs for this week
  field_tiffs <- find_per_field_tiffs_for_week(field_tiles_ci_dir, dates$days_filter)
  
  if (length(field_tiffs) == 0) {
    safe_log("No per-field TIFFs found for this week - returning empty list", "WARNING")
    return(character())
  }
  
  safe_log(paste("Processing", length(field_tiffs), "fields..."))
  
  created_files <- character()
  
  # Initialize progress bar
  pb <- txtProgressBar(min = 0, max = length(field_tiffs), style = 3, width = 50)
  counter <- 0
  
  # Process each field
  for (field_name in names(field_tiffs)) {
    counter <- counter + 1
    tiff_files <- field_tiffs[[field_name]]
    
    # Create composite
    composite <- create_field_weekly_composite(tiff_files, field_name)
    
    if (!is.null(composite)) {
      # Save
      saved_path <- save_field_weekly_mosaic(
        composite, 
        output_dir, 
        field_name, 
        dates$week, 
        dates$year
      )
      
      if (!is.null(saved_path)) {
        created_files <- c(created_files, saved_path)
      }
    }
    
    setTxtProgressBar(pb, counter)
  }
  
  close(pb)
  cat("\n")  # New line after progress bar
  safe_log(paste("✓ Completed: Created", length(created_files), "weekly field mosaics"))
  
  return(created_files)
}
