library(dplyr)
library(tidyselect)
library(stringr)
library(tidyr)
library(readxl)
library(purrr)
library(glue)
library(arrow)

valida_arquivo <- function(path, nome) {
  if (!file.exists(path)) {
    stop(glue("Arquivo nao encontrado para {nome}: {path}"), call. = FALSE)
  }
}

valida_diretorio <- function(path, nome) {
  if (!dir.exists(path)) {
    stop(glue("Diretorio nao encontrado para {nome}: {path}"), call. = FALSE)
  }
}

padroniza_objeto <- function(x) {
  if (is.list(x) && !is.data.frame(x)) {
    return(map(x, padroniza_objeto))
  }

  if (inherits(x, "Table") || inherits(x, "ArrowTabular")) {
    x <- as.data.frame(x)
  }

  if (is.data.frame(x)) {
    x <- as.data.frame(x, stringsAsFactors = FALSE)
  }

  x
}

objetos_iguais <- function(atual, snapshot) {
  atual <- padroniza_objeto(atual)
  snapshot <- padroniza_objeto(snapshot)

  isTRUE(all.equal(atual, snapshot, check.attributes = FALSE))
}

cria_resultado_comparacao <- function(nome, atual, snapshot) {
  tibble::tibble(
    arquivo = nome,
    mensagem = if (objetos_iguais(atual, snapshot)) "iguais" else "diferentes"
  )
}

# funcao para obter linhas com mudancas de codigo em cada revisao NCM
filtra_mudancas <- function(x, padrao) {
  x |>
    select(matches(padrao)) |>
    filter(
      # seleciona as linhas que tem algum valor diferente de NA, excluindo
      # a primeira coluna da comparacao
      if_any(-1, ~ !is.na(.))
    ) |>
    # cria uma coluna preenchida com o primeiro valor nao NA da
    # direita para a esquerda
    mutate(!!padrao := do.call(coalesce, rev(as.list(across(-1))))) |>
    select(
      ncm_original = 1,
      last_col()
    ) |>
    distinct()
}

cria_padrao <- function(x) {
  paste0(x, "$", collapse = "|")
}

# carrega caminhos para dados
general_dir <- Sys.getenv("general")
if (!nzchar(general_dir)) {
  stop("A variavel de ambiente general esta vazia.", call. = FALSE)
}

valida_diretorio(general_dir, "general")

path_ncm <- file.path(
  general_dir,
  "Bases",
  "correlacoes",
  "mercosul",
  "output",
  "correlacao_completa_ncm.xlsx"
)

path_ncm_detalhada <- file.path(
  general_dir,
  "Bases",
  "correlacoes",
  "mercosul",
  "output",
  "correlacao_completa_ncm_detalhada.xlsx"
)

path_naladi_ncm_manual <- file.path(
  general_dir,
  "Bases",
  "correlacoes",
  "aladi",
  "naladi_ncm",
  "correlacao_ncm_naladi_res_812.xlsx"
)

path_naladi <- file.path(
  general_dir,
  "Bases",
  "correlacoes",
  "aladi",
  "naladi_naladi",
  "output",
  "correlacao_completa_naladi_ncmtracker.Rda"
)

path_descricoes <- file.path(
  general_dir,
  "Bases",
  "NCM_sh1996_a_sh2022",
  "raw",
  "tabela_nomenclatura.parquet"
)

# Na pasta dados_gerados, "snapshot" identifica a ultima versao gerada
# e salva para servir de referencia nas comparacoes com os arquivos atuais.
path_ncms_originais_snapshot <- "dados_gerados/ncms_originais.rds"
path_dataset_ncm_snapshot <- "dados_gerados/dataset_ncm.parquet"
path_dataset_naladi_ncm_snapshot <- "dados_gerados/dataset_naladi_ncm.parquet"
path_dataset_naladi_snapshot <- "dados_gerados/dataset_naladi.parquet"
path_descricoes_ncm_snapshot <- "dados_gerados/descricoes_ncm.parquet"

arquivos_necessarios <- c(
  correlacao_ncm = path_ncm,
  correlacao_ncm_detalhada = path_ncm_detalhada,
  correlacao_naladi_ncm_manual = path_naladi_ncm_manual,
  correlacao_naladi = path_naladi,
  descricoes_ncm = path_descricoes,
  ncms_originais_snapshot = path_ncms_originais_snapshot,
  dataset_ncm_snapshot = path_dataset_ncm_snapshot,
  dataset_naladi_ncm_snapshot = path_dataset_naladi_ncm_snapshot,
  dataset_naladi_snapshot = path_dataset_naladi_snapshot,
  descricoes_ncm_snapshot = path_descricoes_ncm_snapshot
)

walk2(
  arquivos_necessarios,
  names(arquivos_necessarios),
  valida_arquivo
)

# reconstroi em memoria o objeto salvo em ncms_originais.rds
correlacao_ncm_detalhada <- read_xlsx(path_ncm_detalhada, guess_max = 1e6)

nomes_para_funcao <- names(correlacao_ncm_detalhada) |>
  str_extract("NCM_\\d{4}") |>
  unique()

ncms_originais_atual <- nomes_para_funcao |>
  map(~ filtra_mudancas(correlacao_ncm_detalhada, padrao = .x))

names(ncms_originais_atual) <- nomes_para_funcao |>
  str_replace("_", " ")

ncms_originais_snapshot <- readRDS(path_ncms_originais_snapshot)

resultado_ncms_originais <- cria_resultado_comparacao(
  "ncms_originais.rds",
  ncms_originais_atual,
  ncms_originais_snapshot
)

# aplica em memoria a mesma transformacao usada para dataset_ncm.parquet
correlacao_completa_ncm_mercosul <- read_xlsx(path_ncm, guess_max = 1e6)

dataset_ncm_atual <- correlacao_completa_ncm_mercosul |>
  distinct() |>
  rename_with(~ str_to_upper(.x)) |>
  tidyr::drop_na()

dataset_ncm_snapshot <- arrow::read_parquet(path_dataset_ncm_snapshot)

resultado_dataset_ncm <- cria_resultado_comparacao(
  "dataset_ncm.parquet",
  dataset_ncm_atual,
  dataset_ncm_snapshot
)

# seleciona a revisao mais recente da correlacao manual NALADI-NCM
naladi_ncm_manual <- read_xlsx(
  path_naladi_ncm_manual,
  guess_max = 1e6
)

colunas_naladi_ncm <- names(naladi_ncm_manual)

ano_max <- colunas_naladi_ncm |>
  str_extract("\\d{4}") |>
  max(na.rm = TRUE) |>
  unique()

dataset_naladi_ncm_atual <- naladi_ncm_manual |>
  select(contains(ano_max)) |>
  distinct()

dataset_naladi_ncm_snapshot <- arrow::read_parquet(path_dataset_naladi_ncm_snapshot)

resultado_dataset_naladi_ncm <- cria_resultado_comparacao(
  "dataset_naladi_ncm.parquet",
  dataset_naladi_ncm_atual,
  dataset_naladi_ncm_snapshot
)

# carrega o .Rda em ambiente separado e reconstroi o parquet correspondente
ambiente_naladi <- new.env(parent = emptyenv())
load(path_naladi, envir = ambiente_naladi)

dataset_naladi_atual <- ambiente_naladi$correlacao_completa_naladi_naladi_ncmtracker |>
  distinct() |>
  rename_with(~ str_to_upper(.x)) |>
  mutate(NALADI_1983 = str_remove_all(NALADI_1983, "\\.")) |>
  mutate(NALADI_1983 = str_c(
    str_sub(NALADI_1983, 1, 2),
    ".",
    str_sub(NALADI_1983, 3, 4),
    ".",
    str_sub(NALADI_1983, 5, 5),
    ".",
    str_sub(NALADI_1983, 6, 7)
  ))

dataset_naladi_snapshot <- arrow::read_parquet(path_dataset_naladi_snapshot)

resultado_dataset_naladi <- cria_resultado_comparacao(
  "dataset_naladi.parquet",
  dataset_naladi_atual,
  dataset_naladi_snapshot
)

# reconstroi em memoria a tabela final de descricoes NCM
descricoes_raw <- arrow::read_parquet(path_descricoes) |>
  select(
    codigo,
    nomeExtenso,
    inicioVigencia,
    inicioVigenciaStr,
    fimVigencia,
    fimVigenciaStr
  ) |>
  mutate(
    grupo_sh = case_when(
      str_detect(fimVigenciaStr, cria_padrao(1996:2001)) ~ "1996",
      str_detect(fimVigenciaStr, cria_padrao(2002:2006)) ~ "2002",
      str_detect(fimVigenciaStr, cria_padrao(2007:2011)) ~ "2007",
      str_detect(fimVigenciaStr, cria_padrao(2012:2016)) ~ "2012",
      fimVigencia == 1648695600000 ~ "2017",
      TRUE ~ "2022"
    )
  ) |>
  group_by(grupo_sh, codigo) |>
  slice_max(order_by = fimVigencia, n = 1) |>
  ungroup()

x <- descricoes_raw |>
  filter(nchar(codigo) != 2) |>
  select(codigo, nomeExtenso, grupo_sh) |>
  mutate(n = nchar(codigo))

sh4 <- x |>
  filter(n == 4) |>
  select(-n) |>
  rename(sh4 = codigo) |>
  rename(desc_sh4 = nomeExtenso) |>
  mutate(desc_sh4 = str_squish(desc_sh4))

sh5 <- x |>
  filter(n == 5) |>
  select(-n) |>
  rename(sh5 = codigo) |>
  rename(desc_sh5 = nomeExtenso) |>
  mutate(desc_sh5 = str_squish(desc_sh5))

sh6 <- x |>
  filter(n == 6) |>
  select(-n) |>
  rename(sh6 = codigo) |>
  rename(desc_sh6 = nomeExtenso) |>
  mutate(desc_sh6 = str_squish(desc_sh6))

sh7 <- x |>
  filter(n == 7) |>
  select(-n) |>
  rename(sh7 = codigo) |>
  rename(desc_sh7 = nomeExtenso) |>
  mutate(desc_sh7 = str_squish(desc_sh7))

descricoes_ncm_atual <- x |>
  filter(n == 8) |>
  select(-n) |>
  mutate(
    sh4 = str_sub(codigo, 1, 4),
    sh5 = str_sub(codigo, 1, 5),
    sh6 = str_sub(codigo, 1, 6),
    sh7 = str_sub(codigo, 1, 7),
    nomeExtenso = str_replace(nomeExtenso, "^- ", "-")
  ) |>
  mutate(
    nomeExtenso = str_replace(nomeExtenso, "^-- ", "--")
  ) |>
  left_join(sh4, by = c("sh4", "grupo_sh")) |>
  left_join(sh5, by = c("sh5", "grupo_sh")) |>
  left_join(sh6, by = c("sh6", "grupo_sh")) |>
  left_join(sh7, by = c("sh7", "grupo_sh")) |>
  select(-matches("^sh")) |>
  mutate(
    across(
      starts_with("desc_sh"),
      ~ str_replace(.x, "^- ", "-")
    ),
    across(
      starts_with("desc_sh"),
      ~ str_replace(.x, "^-- ", "--")
    )
  ) |>
  unite(
    col = descricao,
    starts_with("desc_sh"), nomeExtenso,
    sep = "\n",
    na.rm = TRUE
  ) |>
  select(
    ncm = codigo,
    sh = grupo_sh,
    descricao
  ) |>
  mutate(
    sh = paste("NCM", sh),
    ncm = paste0(
      str_sub(ncm, 1, 4), ".",
      str_sub(ncm, 5, 6), ".",
      str_sub(ncm, 7, 8)
    )
  ) |>
  mutate(
    descricao = str_replace_all(descricao, "'", "\\\\'")
  ) |>
  mutate(
    descricao = str_replace_all(descricao, "\\r\\n", " "),
    descricao = str_trunc(descricao, 1020, "left")
  )

descricoes_ncm_snapshot <- arrow::read_parquet(path_descricoes_ncm_snapshot)

resultado_descricoes_ncm <- cria_resultado_comparacao(
  "descricoes_ncm.parquet",
  descricoes_ncm_atual,
  descricoes_ncm_snapshot
)

# organiza as saidas finais em uma tabela unica
tabela_comparacao <- bind_rows(
  resultado_ncms_originais,
  resultado_dataset_ncm,
  resultado_dataset_naladi_ncm,
  resultado_dataset_naladi,
  resultado_descricoes_ncm
)

print(tabela_comparacao)
