# 1. Filtragem e desbaste de dados

library(dplyr)
library(readr)
library(sf)
library(CoordinateCleaner)

lista_sp <- read.csv("./vascular_epiphyte_data.csv", sep = ";") %>%
  mutate(nome_cientifico = paste(genero, especie))

amazonia <- st_read("./amazonia/amazonpan/Lim_Biogeografico.shp") %>% 
  st_transform(crs = 4326) %>% 
  st_make_valid()

processar_especie <- function(nome_sp, shape_ref) {
  tryCatch({
    res <- occ_search(scientificName = nome_sp, hasCoordinate = TRUE, limit = 5000) 
    if (is.null(res$data) || nrow(res$data) == 0) return(NULL)
    
    dados <- res$data %>%
      dplyr::select(species, decimalLongitude, decimalLatitude) %>%
      filter(!is.na(decimalLongitude) & !is.na(decimalLatitude))
    
    dados_clean <- clean_coordinates(
      x = dados, lon = "decimalLongitude", lat = "decimalLatitude",
      species = "species", value = "clean",
      tests = c("capitals", "centroids", "equal", "gbif", "institutions", "zeros"),
      verbose = FALSE
    )
    
    if (nrow(dados_clean) == 0) return(NULL)
    
    pontos_sf <- st_as_sf(dados_clean, coords = c("decimalLongitude", "decimalLatitude"), crs = 4326)
    intersecao <- st_intersects(pontos_sf, shape_ref)
    
    resumo <- data.frame(
      species = nome_sp,
      total_ocorrencias = nrow(dados_clean),
      coords_na_amazonia = sum(lengths(intersecao) > 0)
    ) %>%
      mutate(apenas_na_amazonia = ifelse(coords_na_amazonia == total_ocorrencias, "SIM", "NÃO"))
    
    return(resumo)
  }, error = function(e) {
    cat("Erro na espécie:", nome_sp, "\n")
    return(NULL)
  })
}

tamanho_lote <- 200
total_especies <- nrow(lista_sp)
indices_lote <- split(1:total_especies, ceiling(seq_along(1:total_especies)/tamanho_lote))

cat("Iniciando processamento de", total_especies, "espécies em", length(indices_lote), "lotes.\n")

for (i in seq_along(indices_lote)) {
  cat("\n--- Processando Lote", i, "de", length(indices_lote), "---\n")
  
  indices_atuais <- indices_lote[[i]]
  nomes_atuais <- lista_sp$nome_cientifico[indices_atuais]
  
  resultados_lote <- lapply(nomes_atuais, function(x) processar_especie(x, amazonia))
  df_lote <- bind_rows(resultados_lote)
  
  if (nrow(df_lote) > 0) {
    write_csv(df_lote, file.path(output_dir, paste0("lote_", i, ".csv")))
  }
  
  rm(resultados_lote, df_lote)
  gc() 
}

cat("\nConsolidando arquivos finais...\n")
arquivos_parciais <- list.files(output_dir, pattern = "*.csv", full.names = TRUE)
resumo_final <- map_df(arquivos_parciais, read_csv)

write_csv(resumo_final, "resumo_especies_amazonia_FINAL.csv")
cat("Concluído! O arquivo final tem", nrow(resumo_final), "espécies analisadas.\n")

specie_filter <- resumo_final %>%
  filter(grepl("Bifrenaria longicornis", name, ignore.case = TRUE)) %>%
  dplyr::select(-prov, -date, -key) 

raster_base <- rast("var/wc2.1_2.5m_bio/bio01.tif")
coords <- specie_filter %>% dplyr::select(longitude, latitude)
cell_ids <- cellFromXY(raster_base, as.matrix(coords))
specie_filter_unique <- specie_filter %>%
  mutate(cell_id = cell_ids) %>%
  filter(!is.na(cell_id)) %>%     
  group_by(cell_id) %>%
  slice_sample(n = 1) %>% 
  ungroup() %>%
  dplyr::select(-cell_id)

write.csv(specie_filter_unique, "ocorrencias_Bifrenaria_longicornis_clean", row.names = FALSE)

# 2. Análise de correlação e multicolinariedade
library(terra)
library(tidyverse)
col <- colorRampPalette(c("#D00000", "#FFBA08", "#3F88C5", "#032B43"))

# 2.1. Carregar dados -------------------------------------------------------

amazon <- sf::st_read("amazonpan/Limites_RAISG_2025/Lim_Biogeografico.shp") 
amazon <- sf::st_transform(amazon, 4326)

data <- read.csv2("occ/ocorrencias_Bifrenaria_longicornis_clean.csv")
family_filter <- data %>%
  mutate(across(c(Longitude, Latitude), as.numeric)) %>% 
  select(Longitude, Latitude) # Mantendo apenas as colunas de coordenadas

#### bio
env <- rast(
  list.files(
    path = "var/wc2.1_2.5m_bio/",
    pattern = ".tif$",
    full.names = TRUE
  )
)

env <- mask(crop(env, amazon), amazon)

# 2.2 Amostragem combinada (Aleatória + Ocorrências) -----------------------

# A. Amostra aleatória do background (espaço geográfico)
env_bg_sample <- spatSample(env, size = 10000, method = "random", na.rm = TRUE)

# B. Extração nos pontos de ocorrência (espaço da espécie)
# Convertendo para SpatVector para garantir compatibilidade com o terra
occ_points <- vect(family_filter, geom = c("Longitude", "Latitude"), crs = "EPSG:4326")
env_occ_sample <- terra::extract(env, occ_points, ID = FALSE, na.rm = TRUE)

# C. Combinar ambas as amostras para o teste de correlação
# Isso garante que a correlação considere o nicho ocupado e o disponível
env_sample <- rbind(env_bg_sample, env_occ_sample)

# 2.3. Filtro de correlação -------------------------------------------------

corr_matrix <- cor(env_sample, method = "pearson")

corrplot::corrplot( 
  corr_matrix,
  method = "color",
  type = "upper",
  tl.cex = 0.7,
  tl.col = "black",
  addCoef.col = "white",
  number.cex = 0.7,
  col = col(100)
)

# Extrair índices com valor de correlação maior que o threshold (0.8)
high_corr <- caret::findCorrelation(corr_matrix, cutoff = 0.8) 

high_corr_pairs <- which(abs(corr_matrix) > 0.8 & upper.tri(corr_matrix), arr.ind = TRUE)

corr_pairs_tbl <- tibble(
  var1 = rownames(corr_matrix)[high_corr_pairs[, "row"]],
  var2 = colnames(corr_matrix)[high_corr_pairs[, "col"]],
  corr = corr_matrix[high_corr_pairs]
)

corr_pairs_tbl <- corr_pairs_tbl %>% 
  mutate(
    drop_var1 = var1 %in% colnames(corr_matrix)[high_corr],
    drop_var2 = var2 %in% colnames(corr_matrix)[high_corr]
  ) %>%
  arrange(desc(corr))

# Remover variáveis correlacionadas
env_corr_selection <- env[[-high_corr]] 

# 2.4. Filtro VIF (opcionalmente usando a amostra combinada) ----------------

# Para o VIF ser consistente com a amostra acima:
vif <- usdm::vifstep(env_sample[, -high_corr], th = 5) 

vif 

# Aplicar a exclusão final no RasterStack
env_final_selection <- usdm::exclude(env_corr_selection, vif)

# Visualizar as variáveis finais
names(env_final_selection)

#3. Modelagem de nicho ecológico
setwd("C:/Users/Ana Laura/Documents/Modelagem/manuscrito/")

library(terra)
library(tidyverse)
library(sf)
library(caret)
library(usdm)
library(corrplot)

# 3.1 DATA
data <- read.csv2("occ/ocorrencias_Bifrenaria_longicornis_clean.csv") # matriz de ocorrências
family_filter <- data %>%
  mutate(across(c(Longitude, Latitude), as.numeric)) %>% # converter para número
  select(-species)
amazon <- sf::st_read("amazonpan/Limites_RAISG_2025/Lim_Biogeografico.shp") # shapefile amazônia
amazon <- sf::st_transform(amazon, 4326) # converter para WGS84 (original SIRGAS 2000)
env <- rast(
  list.files(
    path = "var/wc2.1_2.5m_bio/",
    pattern = ".tif$",
    full.names = TRUE
  )
)
env <- mask(crop(env, amazon), amazon) # fazer máscara e recorte para Amazônia
env_final_selection <- subset(env, c(2, 3, 4, 10, 15, 18, 19))
names(env_final_selection)
library(ENMeval)

# 3.2. Processo com ENMeval -------------------------------------------------

## Criar objeto de background

bg <- spatSample(
  env_final_selection,
  size = 10000,
  method = "random",
  xy = TRUE,
  na.rm = TRUE
) %>%
  select(Longitude = x, Latitude = y)

## Treinar modelos candidatos

tune <- ENMevaluate(
  occs = family_filter[, c("Longitude", "Latitude")],
  envs = env_final_selection,
  bg = bg,
  partitions = "block", #list(fc = c("L", "LQ", "LQH"), rm = seq(0.5, 4, 0.5))
  tune.args = list(
    rm = seq(0.5, 5, by = 0.5), # regularizadores
    fc = c("L", "LQ", "LQH") # feature classes
  ),
  algorithm = "maxent.jar",
  parallel = FALSE # definir uso de paralelização
)


# 3.3. Pós-modelagem --------------------------------------------------------

## Extrair matriz de resultados

results <- eval.results(tune)
view(results)
library(writexl)

write_xlsx(results, "results2/Bifrenaria_longicornis/resultados_modelagem_ENMeval_Bifrenaria_longicornis.xlsx")

## Extrair configurações do "melhor modelo"
#best_res <- tune@results[which.min(tune@results$"rm.1_fc.LQ")] #seleção por modelo

best_res <- tune@results[which.min(tune@results$delta.AICc), ] #seleção por melhor delta.AICc

## Extrair projeção 

current_proj <- eval.predictions(tune)[["rm.1_fc.LQ"]] 

plot(eval.predictions(tune)[["rm.1_fc.LQ"]], col = terrain.colors(100))
points(family_filter, col = "red")

## Extrair objeto MaxEnt do "melhor modelo"

best_model <- tune@models[["rm.1_fc.LQ"]]
plot(best_model)

# 1. Extrair os resultados do modelo
# O MaxEnt guarda as métricas no slot @results
res_mat <- best_model@results

# 2. Filtrar apenas as linhas de contribuição percentual
cont_rows <- grep(".contribution", rownames(res_mat))
df_cont <- data.frame(
  Variavel = rownames(res_mat)[cont_rows],
  Contribuicao = as.numeric(res_mat[cont_rows])
)

# 3. Limpar os nomes das variáveis (remover o sufixo '.contribution')
df_cont$Variavel <- gsub(".contribution", "", df_cont$Variavel)

# 4. Ordenar os dados para o gráfico
df_cont <- df_cont %>%
  arrange(desc(Contribuicao)) %>%
  mutate(Variavel = factor(Variavel, levels = rev(Variavel)))

# Criar o gráfico
tiff("./results2/Variavel_Contribution_Bifrenaria_longicornis.tif", 
     width = 15, height = 12, units = "cm", 
     res = 300, compression = "lzw")

ggplot(df_cont, aes(x = Variavel, y = Contribuicao)) +
  geom_bar(stat = "identity", fill = "steelblue", color = "black", width = 0.5) +
  # O clip = "off" impede que elementos próximos às bordas sejam cortados
  coord_flip(clip = "off") +
  # expand = expansion(mult = c(0, 0.05)) adiciona um pequeno respiro no final do eixo
  scale_y_continuous(limits = c(0, 100), expand = expansion(mult = c(0, 0.05))) + 
  theme_minimal() +
  labs(title = "Bifrenaria longicornis",
       x = "Bioclimatic Variables",
       y = "Percentage (%)") +
  theme(
    plot.title = element_text(hjust = 0.5, face = "italic", size = 12), 
    axis.text = element_text(color = "black"),
    panel.grid.minor = element_blank(),
    # Adiciona margens extras (topo, direita, baixo, esquerda) para evitar cortes
    plot.margin = margin(t = 10, r = 20, b = 10, l = 10, unit = "pt")
  )

dev.off()

print("Gráfico gerado com sucesso")


# 3.4. Projeções futuras ----------------------------------------------------

## Download do GCM

env_miroc6 <- rast("var/wc2.1_2.5m_bioc_MIROC6_ssp245_2061-2080.tif")
env_ec_earth <- rast("var/wc2.1_2.5m_bioc_EC-Earth3-Veg_ssp245_2061-2080.tif")
env_mpi <- rast("var/wc2.1_2.5m_bioc_MPI-ESM1-2-HR_ssp245_2061-2080.tif")
names(env_miroc6) <- paste0("bio", sprintf("%02d", 01:19))
names(env_ec_earth) <- paste0("bio", sprintf("%02d", 01:19))
names(env_mpi) <- paste0("bio", sprintf("%02d", 01:19))

vars_model <- names(env_final_selection)
names(env_final_selection)

prep_env <- function(env){
  
  env <- mask(crop(env, amazon), amazon)
  
  bio_vars <- env[[c("bio01", "bio02", "bio03", "bio04",      
                     "bio14", "bio16", "bio18", "bio19")]]
  
  env_full <- c(bio_vars)
  
  env_full <- env_full[[vars_model]]
  
  return(env_full)
}

env_miroc6_sel   <- prep_env(env_miroc6)
env_ec_earth_sel <- prep_env(env_ec_earth)
env_mpi_sel      <- prep_env(env_mpi)

proj_miroc6 <- predict(env_miroc6_sel, best_model, type = "cloglog")
proj_ec     <- predict(env_ec_earth_sel, best_model, type = "cloglog")
proj_mpi    <- predict(env_mpi_sel, best_model, type = "cloglog")

ensemble <- app(c(proj_miroc6, proj_ec, proj_mpi), 
                fun = median, na.rm = TRUE)

diff_raster <- ensemble - current_proj
plot(diff_raster)

vals_current <- values(current_proj)[,1]
vals_future  <- values(ensemble)[,1]

diff_vals <- vals_future - vals_current

change_tbl <- tibble(
  change = case_when(
    diff_vals > 0 ~ "Increase",
    diff_vals < 0 ~ "Decrease",
    TRUE ~ "No change"
  )
) %>%
  count(change) %>%
  mutate(percent = n / sum(n) * 100)

change_tbl

library(ggspatial)
library(terra)
library(tidyverse)

spectral_col <- rev(RColorBrewer::brewer.pal(10, "Spectral"))

amazon <- sf::st_read("amazonpan/Limites_RAISG_2025/Lim_Biogeografico.shp") # shapefile amazônia

amazon <- sf::st_transform(amazon, 4326) # converter para WGS84 (original SIRGAS 2000)

# converter para SpatVector e alinhar CRS com raster
amazon_vect <- vect(amazon)
amazon_vect <- project(amazon_vect, crs(current_proj))

rast_to_df <- function(r){
  df <- as.data.frame(r, xy = TRUE, na.rm = TRUE)
  names(df) <- c("lon", "lat", "value")
  return(df)
}

current_suitab <- rast_to_df(current_proj)
future_suitab  <- rast_to_df(ensemble)
diff_suitab    <- rast_to_df(diff_raster)

env_raster <- env_final_selection

g <- global(env_raster, "sum", na.rm = TRUE)[1, ]

print(g)

if (any(is.na(g))) {
  warning("Algumas camadas têm NA")
}

if (all(is.na(g))) {
  stop("env_raster completamente vazio")
}
crs(env_final_selection)
crs(current_proj)
crs(amazon_vect)

model_eval <- dismo::evaluate(
  p = family_filter[, c("Longitude", "Latitude")],
  a = bg,
  model = best_model,
  x = env_raster
)

g <- global(env_raster, "sum", na.rm = TRUE)[1, ]

if (all(is.na(g))) {
  stop("Raster vazio — revisar crop/mask")
}
# Extrair valores
p_vals <- terra::extract(env_raster, family_filter[, c("Longitude","Latitude")])
a_vals <- terra::extract(env_raster, bg)

# Remover NA
p_vals <- na.omit(p_vals)
a_vals <- na.omit(a_vals)

# Predição
p_pred <- predict(best_model, p_vals)
a_pred <- predict(best_model, a_vals)

# Avaliação
model_eval <- dismo::evaluate(p = p_pred, a = a_pred)

# Métricas
auc <- model_eval@auc
thresholds <- dismo::threshold(model_eval)

tss <- thresholds[["spec_sens"]]

auc
tss

current_binary <- current_proj >= tss

future_binary <- ensemble >= tss

change_map <- future_binary - current_binary

change_map_class <- classify(change_map, rbind(
  c(-1, -1, 1),  # perda
  c(0, 0, 2),    # estável
  c(1, 1, 3)     # ganho
))

vals <- values(change_map, na.rm = TRUE)

change_tbl <- tibble(
  change = case_when(
    vals == 1 ~ "Gain",
    vals == -1 ~ "Loss",
    vals == 0 ~ "Stable"
  )
) %>%
  count(change) %>%
  mutate(percent = n / sum(n) * 100)

change_tbl

bin_stack <- c(
  proj_miroc6 >= tss,
  proj_ec >= tss,
  proj_mpi >= tss
)

consensus <- app(bin_stack, sum)

# Presença se >= 2 modelos concordam
consensus_binary <- consensus >= 2
plot(consensus_binary)

#dir.create("rasters_qgis", showWarnings = FALSE)
#atual
writeRaster(current_proj,
            "results2/Bifrenaria_longicornis/Bifrenaria_longicornis_current.tif",
            overwrite = TRUE,
            filetype = "GTiff",
            gdal = c("COMPRESS=LZW"))
#futuro
writeRaster(ensemble,
            "results2/Bifrenaria_longicornis/Bifrenaria_longicornis_trollii_245.tif",
            overwrite = TRUE,
            filetype = "GTiff",
            gdal = c("COMPRESS=LZW"))
#binario atual
current_binary <- current_proj >= tss

writeRaster(current_binary,
            "results2/Bifrenaria_longicornis/Bifrenaria_longicornis_binary_current.tif",
            overwrite = TRUE,
            filetype = "GTiff",
            datatype = "INT1U",
            gdal = c("COMPRESS=LZW"))
#binario futuro

future_binary <- ensemble >= tss

writeRaster(future_binary,
            "results2/Bifrenaria_longicornis/Bifrenaria_longicornis_binary_245.tif",
            overwrite = TRUE,
            filetype = "GTiff",
            datatype = "INT1U",
            gdal = c("COMPRESS=LZW"))
#binario mudança
change_map <- future_binary - tss

writeRaster(change_map,
            "results2/Bifrenaria_longicornis/Bifrenaria_longicornis_change_binary_245_map.tif",
            overwrite = TRUE,
            filetype = "GTiff",
            datatype = "INT1S",
            gdal = c("COMPRESS=LZW"))

####585
env_miroc6 <- rast("var/wc2.1_2.5m_bioc_MIROC6_ssp585_2061-2080.tif")
env_ec_earth <- rast("var/wc2.1_2.5m_bioc_EC-Earth3-Veg_ssp585_2061-2080.tif")
env_mpi <- rast("var/wc2.1_2.5m_bioc_MPI-ESM1-2-HR_ssp585_2061-2080.tif")
names(env_miroc6) <- paste0("bio", sprintf("%02d", 01:19))
names(env_ec_earth) <- paste0("bio", sprintf("%02d", 01:19))
names(env_mpi) <- paste0("bio", sprintf("%02d", 01:19))

vars_model <- names(env_final_selection)
names(env_final_selection)

prep_env <- function(env){
  
  env <- mask(crop(env, amazon), amazon)
  
  bio_vars <- env[[c("bio01", "bio02", "bio03", "bio04",      
                     "bio14", "bio16", "bio18", "bio19")]]                  
  env_full <- c(bio_vars)
  
  env_full <- env_full[[vars_model]]
  
  return(env_full)
}

env_miroc6_sel   <- prep_env(env_miroc6)
env_ec_earth_sel <- prep_env(env_ec_earth)
env_mpi_sel      <- prep_env(env_mpi)

proj_miroc6 <- predict(env_miroc6_sel, best_model, type = "cloglog")
proj_ec     <- predict(env_ec_earth_sel, best_model, type = "cloglog")
proj_mpi    <- predict(env_mpi_sel, best_model, type = "cloglog")

ensemble <- app(c(proj_miroc6, proj_ec, proj_mpi), 
                fun = median, na.rm = TRUE)

diff_raster <- ensemble - current_proj
plot(diff_raster)

vals_current <- values(current_proj)[,1]
vals_future  <- values(ensemble)[,1]

diff_vals <- vals_future - vals_current

change_tbl <- tibble(
  change = case_when(
    diff_vals > 0 ~ "Increase",
    diff_vals < 0 ~ "Decrease",
    TRUE ~ "No change"
  )
) %>%
  count(change) %>%
  mutate(percent = n / sum(n) * 100)

change_tbl

library(ggspatial)
library(terra)
library(tidyverse)

spectral_col <- rev(RColorBrewer::brewer.pal(10, "Spectral"))

amazon <- sf::st_read("amazonpan/Limites_RAISG_2025/Lim_Biogeografico.shp") # shapefile amazônia

amazon <- sf::st_transform(amazon, 4326) # converter para WGS84 (original SIRGAS 2000)

# converter para SpatVector e alinhar CRS com raster
amazon_vect <- vect(amazon)
amazon_vect <- project(amazon_vect, crs(current_proj))

rast_to_df <- function(r){
  df <- as.data.frame(r, xy = TRUE, na.rm = TRUE)
  names(df) <- c("lon", "lat", "value")
  return(df)
}

current_suitab <- rast_to_df(current_proj)
future_suitab  <- rast_to_df(ensemble)
diff_suitab    <- rast_to_df(diff_raster)

env_raster <- env_final_selection

g <- global(env_raster, "sum", na.rm = TRUE)[1, ]

print(g)

if (any(is.na(g))) {
  warning("Algumas camadas têm NA")
}

if (all(is.na(g))) {
  stop("env_raster completamente vazio")
}
crs(env_final_selection)
crs(current_proj)
crs(amazon_vect)

model_eval <- dismo::evaluate(
  p = family_filter[, c("Longitude", "Latitude")],
  a = bg,
  model = best_model,
  x = env_raster
)

g <- global(env_raster, "sum", na.rm = TRUE)[1, ]

if (all(is.na(g))) {
  stop("Raster vazio — revisar crop/mask")
}
# Extrair valores
p_vals <- terra::extract(env_raster, family_filter[, c("Longitude","Latitude")])
a_vals <- terra::extract(env_raster, bg)

# Remover NA
p_vals <- na.omit(p_vals)
a_vals <- na.omit(a_vals)

# Predição
p_pred <- predict(best_model, p_vals)
a_pred <- predict(best_model, a_vals)

# Avaliação
model_eval <- dismo::evaluate(p = p_pred, a = a_pred)

# Métricas
auc <- model_eval@auc
thresholds <- dismo::threshold(model_eval)

tss <- thresholds[["spec_sens"]]

auc
tss

current_binary <- current_proj >= tss

future_binary <- ensemble >= tss

change_map <- future_binary - current_binary

change_map_class <- classify(change_map, rbind(
  c(-1, -1, 1),  # perda
  c(0, 0, 2),    # estável
  c(1, 1, 3)     # ganho
))

vals <- values(change_map, na.rm = TRUE)

change_tbl <- tibble(
  change = case_when(
    vals == 1 ~ "Gain",
    vals == -1 ~ "Loss",
    vals == 0 ~ "Stable"
  )
) %>%
  count(change) %>%
  mutate(percent = n / sum(n) * 100)

change_tbl

bin_stack <- c(
  proj_miroc6 >= tss,
  proj_ec >= tss,
  proj_mpi >= tss
)

consensus <- app(bin_stack, sum)

# Presença se >= 2 modelos concordam
consensus_binary <- consensus >= 2
plot(consensus_binary)

#futuro
writeRaster(ensemble,
            "results2/Bifrenaria_longicornis/Bifrenaria_longicornis_585.tif",
            overwrite = TRUE,
            filetype = "GTiff",
            gdal = c("COMPRESS=LZW"))
future_binary <- ensemble >= tss

writeRaster(future_binary,
            "results2/Bifrenaria_longicornis/Bifrenaria_longicornis_binary_585.tif",
            overwrite = TRUE,
            filetype = "GTiff",
            datatype = "INT1U",
            gdal = c("COMPRESS=LZW"))
#binario mudança
change_map <- future_binary - tss

writeRaster(change_map,
            "results2/Bifrenaria_longicornis/Bifrenaria_longicornis_change_585_map.tif",
            overwrite = TRUE,
            filetype = "GTiff",
            datatype = "INT1S",
            gdal = c("COMPRESS=LZW"))


