#!/usr/bin/env bash
# Pós-deploy no servidor (GitHub Actions ou SSH manual).
#
# Variáveis opcionais:
#   DEPLOY_PATH     — pasta do projeto (padrão: diretório atual)
#   SERVICE_NAME    — unit systemd (padrão: omrcheck-web)
#   PYTHON_BIN      — interpretador (padrão: python3.11 … python3)
#   SKIP_MIGRATE    — se "1", não roda init_database.py
#   SKIP_CLEANUP    — se "1", não executa a limpeza de retenção pós-deploy
#   CREATE_DATABASE — se "1", tenta CREATE DATABASE (VPS/Docker; cPanel não precisa)
#   SKIP_RESTART    — se "1", não reinicia a aplicação
#   RESTART_MODE    — auto (padrão), systemd ou nohup
#   ALLOW_RESTART_FAILURE — se "1", não falha o deploy se o restart falhar
set -euo pipefail

# IMPORTAR_LOTES_FILE_PARA_MYSQL=1 importa uma vez os lotes locais antes do restart.
# IMPORTAR_LOTES_SCHOOLS limita as schools (se vazio, detecta processamentos/keep_*_omrcheck).

# DEPLOY_PATH = pasta no servidor (ex.: .../dev-reading.keepedu.com.br).
# Neste repo back+front ficam na mesma raiz (main.py, app/, web/, scripts/).
REPO_DIR="${DEPLOY_PATH:-${APP_DIR:-$(cd "$(dirname "$0")/.." && pwd)}}"
REPO_DIR="${REPO_DIR%/}"
APP_DIR="${REPO_DIR}"
SERVICE_NAME="${SERVICE_NAME:-omrcheck-web}"
RESTART_MODE="${RESTART_MODE:-auto}"
VENV_DIR="${APP_DIR}/.venv"

if [ -z "${REPO_DIR}" ]; then
  echo "[ERRO] DEPLOY_PATH não definido."
  exit 1
fi

if [ ! -d "${APP_DIR}" ]; then
  echo "[ERRO] Pasta de deploy não existe: ${APP_DIR}"
  echo "Crie no servidor: mkdir -p ${APP_DIR}"
  exit 1
fi

cd "${APP_DIR}"

if [ ! -f "requirements-prod.txt" ]; then
  echo "[ERRO] Arquivos do projeto não encontrados em ${APP_DIR}."
  echo "Verifique o secret DEPLOY_PATH (deve ser a mesma pasta usada no rsync)."
  exit 1
fi

PY=""
for c in "${PYTHON_BIN:-}" python3.13 python3.12 python3.11 python3.10 python3; do
  [ -n "$c" ] || continue
  if command -v "$c" >/dev/null 2>&1; then
    PY="$c"
    break
  fi
done

if [ -z "$PY" ]; then
  echo "[ERRO] Nenhum interpretador Python 3 encontrado no servidor."
  exit 1
fi

echo "==> Interpretador: $PY ($("$PY" --version 2>&1))"

if ! "$PY" -c 'import sys; sys.exit(0 if sys.version_info[:2] >= (3, 11) else 1)'; then
  echo "[ERRO] O projeto exige Python >= 3.11."
  exit 1
fi

echo "==> Virtualenv..."
_venv_precisa_recriar() {
  if [ ! -x "${VENV_DIR}/bin/python" ]; then
    return 0
  fi
  if ! "${VENV_DIR}/bin/python" -c "import sys" >/dev/null 2>&1; then
    return 0
  fi
  if [ -f "${VENV_DIR}/bin/pip" ]; then
    local interp
    interp="$(sed -n '1s/^#![[:space:]]*//p' "${VENV_DIR}/bin/pip" | awk '{print $1}')"
    if [ -n "${interp}" ] && [ ! -e "${interp}" ]; then
      echo "[INFO] .venv foi criado em outro caminho (${interp}). Recriando..."
      return 0
    fi
  fi
  return 1
}

if _venv_precisa_recriar; then
  rm -rf "${VENV_DIR}"
fi

if [ ! -d "${VENV_DIR}" ]; then
  "${PY}" -m venv "${VENV_DIR}"
fi

# shellcheck source=/dev/null
source "${VENV_DIR}/bin/activate"

"${VENV_DIR}/bin/python" -m pip install --upgrade pip
"${VENV_DIR}/bin/python" -m pip install -r requirements-prod.txt

# ZXing e instalado por pip e funciona sem dependencia nativa. pyzbar e
# mantido como alternativa quando libzbar0 tambem estiver disponivel.
if ! "${VENV_DIR}/bin/python" -c 'import zxingcpp' >/dev/null 2>&1; then
  echo "[ERRO] Leitor de codigo de barras ZXing indisponivel apos instalar as dependencias."
  exit 1
fi

if [ ! -f "${REPO_DIR}/.env" ]; then
  echo "[ERRO] Arquivo .env não encontrado em:"
  echo "  ${REPO_DIR}/.env"
  echo ""
  echo "O deploy não envia o .env (fica só no servidor)."
  echo "Crie/edite pelo cPanel File Manager na MESMA pasta do DEPLOY_PATH:"
  echo "  ${REPO_DIR}"
  exit 1
fi

# Os lotes existentes continuam em arquivos. Para evitar que um deploy oculte
# o histórico antes de uma migração completa, o modo seguro é ``file``.
# A migração para MySQL é explícita: DEPLOY_STORAGE_BACKEND=mysql.
STORAGE_BACKEND_DEPLOY="${DEPLOY_STORAGE_BACKEND:-file}"
if grep -q '^APP_STORAGE_BACKEND=' "${REPO_DIR}/.env"; then
  sed -i "s/^APP_STORAGE_BACKEND=.*/APP_STORAGE_BACKEND=${STORAGE_BACKEND_DEPLOY}/" "${REPO_DIR}/.env"
else
  printf '\nAPP_STORAGE_BACKEND=%s\n' "${STORAGE_BACKEND_DEPLOY}" >> "${REPO_DIR}/.env"
fi
echo "==> Armazenamento de lotes: ${STORAGE_BACKEND_DEPLOY}"

if [[ "${SKIP_MIGRATE:-0}" != "1" ]]; then
  MYSQL_ATIVO="$(python -c "from app.db import mysql_habilitado; print('true' if mysql_habilitado() else 'false')")"
  if [[ "${MYSQL_ATIVO}" == "true" ]]; then
      echo "==> Banco + migrations / schema..."
      if [[ "${CREATE_DATABASE:-0}" == "1" ]]; then
        python scripts/init_database.py --criar-banco
      else
        python scripts/init_database.py
      fi
  else
    echo "==> MySQL desabilitado; usando somente armazenamento em arquivos."
  fi
fi

if [[ "${IMPORTAR_LOTES_FILE_PARA_MYSQL:-0}" == "1" ]]; then
  MYSQL_ATIVO="$(python -c "from app.db import mysql_habilitado; print('true' if mysql_habilitado() else 'false')")"
  if [[ "${MYSQL_ATIVO}" != "true" ]]; then
    echo "[ERRO] IMPORTAR_LOTES_FILE_PARA_MYSQL=1 exige MYSQL_ENABLED=true."
    exit 1
  fi

  SCHOOLS_IMPORTACAO="${IMPORTAR_LOTES_SCHOOLS:-}"
  if [[ -z "${SCHOOLS_IMPORTACAO}" && -d "${APP_DIR}/processamentos" ]]; then
    SCHOOLS_IMPORTACAO="$({
      for pasta in "${APP_DIR}"/processamentos/keep_*_omrcheck; do
        [ -d "${pasta}" ] || continue
        nome="$(basename "${pasta}")"
        echo "${nome#keep_}" | sed 's/_omrcheck$//'
      done
    } | paste -sd, -)"
  fi

  if [[ -z "${SCHOOLS_IMPORTACAO}" ]]; then
    echo "[INFO] Nenhuma pasta de school encontrada para importar."
  else
    echo "==> Importando lotes locais para MySQL (${SCHOOLS_IMPORTACAO})..."
    IFS=',' read -r -a escolas <<< "${SCHOOLS_IMPORTACAO}"
    for school in "${escolas[@]}"; do
      school="$(echo "${school}" | xargs)"
      [ -n "${school}" ] || continue
      python scripts/importar_lotes_file_para_mysql.py --school "${school}" --apply
    done
  fi
fi

if [[ "${SKIP_CLEANUP:-0}" != "1" ]]; then
  echo "==> Limpando arquivos expirados (retenção OMR: 7 dias)..."
  "${VENV_DIR}/bin/python" scripts/maintenance.py cleanup
fi

echo "==> Compactando JSONs persistidos..."
"${VENV_DIR}/bin/python" scripts/compactar_json_armazenado.py

echo "==> Removendo PDFs temporários de frequência..."
"${VENV_DIR}/bin/python" scripts/limpar_frequencias_pdf.py
 
_reiniciar_systemd() {
  local servico="$1"

  if [[ "$(id -u)" -eq 0 ]]; then
    systemctl restart "${servico}"
    systemctl is-active --quiet "${servico}"
    return 0
  fi

  if systemctl restart "${servico}" 2>/dev/null; then
    systemctl is-active --quiet "${servico}"
    return 0
  fi

  if sudo -n systemctl restart "${servico}" 2>/dev/null; then
    sudo -n systemctl is-active --quiet "${servico}"
    return 0
  fi

  return 1
}

_validar_diretorio_systemd() {
  local servico="$1"
  local diretorio_servico
  local diretorio_app

  diretorio_servico="$(systemctl show "${servico}" --property=WorkingDirectory --value 2>/dev/null || true)"
  [ -n "${diretorio_servico}" ] || return 0

  diretorio_app="$(cd "${APP_DIR}" && pwd -P)"
  if [ "${diretorio_servico}" != "${diretorio_app}" ]; then
    echo "[ERRO] O servico ${servico} executa outra pasta: ${diretorio_servico}"
    echo "       O deploy foi enviado para: ${diretorio_app}"
    echo "       Ajuste WorkingDirectory e ExecStart do unit systemd para a pasta do DEPLOY_PATH."
    return 1
  fi
}

_porta_desta_app() {
  cd "${APP_DIR}"
  # shellcheck source=/dev/null
  source "${VENV_DIR}/bin/activate"
  python -c "from app.settings import PORT; print(PORT)"
}

_pids_na_porta() {
  local porta="$1"
  ss -H -tlnp 2>/dev/null | grep -E ":${porta}([^0-9]|$)" | grep -oE 'pid=[0-9]+' | cut -d= -f2 | sort -u
}

_encerrar_processos_desta_app() {
  local porta="$1"
  local pid

  for pid in $(_pids_na_porta "${porta}"); do
    echo "[INFO] Encerrando processo antigo na porta ${porta} (PID ${pid})."
    kill "${pid}" 2>/dev/null || true
  done

  sleep 1
  for pid in $(_pids_na_porta "${porta}"); do
    kill -9 "${pid}" 2>/dev/null || true
  done
}

_reiniciar_nohup() {
  local log_dir="${APP_DIR}/logs"
  local log_file="${log_dir}/uvicorn.log"
  local py_bin="${VENV_DIR}/bin/python"
  local porta
  local pid

  mkdir -p "${log_dir}"

  porta="$(_porta_desta_app)"
  if [ -z "${porta}" ]; then
    echo "[ERRO] Não foi possível ler PORT do .env em ${APP_DIR}."
    return 1
  fi

  # Mata só quem está na porta desta instância (4019 prod / 4020 develop).
  _encerrar_processos_desta_app "${porta}"

  cd "${APP_DIR}"
  # shellcheck source=/dev/null
  source "${VENV_DIR}/bin/activate"
  nohup "${py_bin}" main.py >> "${log_file}" 2>&1 &
  pid=$!

  sleep 2
  if kill -0 "${pid}" 2>/dev/null; then
    echo "[OK] Aplicação reiniciada via nohup (PID ${pid}) na porta ${porta}."
    echo "     Log: ${log_file}"
    return 0
  fi

  echo "[ERRO] Processo nohup não sobreviveu ao start."
  tail -n 30 "${log_file}" 2>/dev/null || true
  return 1
}

_ler_health_url() {
  if [ -n "${HEALTH_URL:-}" ]; then
    echo "${HEALTH_URL}"
    return 0
  fi

  python -c "from app.settings import PORT; print(f'http://127.0.0.1:{PORT}/healthz')"
}

if [[ "${SKIP_RESTART:-0}" != "1" ]]; then
  echo "==> Reiniciando aplicação (modo: ${RESTART_MODE})..."
  RESTART_OK=0

  if [[ "${RESTART_MODE}" == "systemd" || "${RESTART_MODE}" == "auto" ]]; then
    if command -v systemctl &>/dev/null && _validar_diretorio_systemd "${SERVICE_NAME}" && _reiniciar_systemd "${SERVICE_NAME}"; then
      echo "[OK] Serviço ${SERVICE_NAME} reiniciado via systemd."
      RESTART_OK=1
    elif [[ "${RESTART_MODE}" == "systemd" ]]; then
      echo "[ERRO] Não foi possível reiniciar ${SERVICE_NAME} via systemd."
      echo "O usuário $(whoami) precisa de root, sudo sem senha ou RESTART_MODE=nohup."
    fi
  fi

  if [[ "${RESTART_OK}" -eq 0 && ( "${RESTART_MODE}" == "nohup" || "${RESTART_MODE}" == "auto" ) ]]; then
    if [[ "${RESTART_MODE}" == "auto" ]]; then
      echo "[INFO] systemd indisponível — usando nohup..."
    fi
    if _reiniciar_nohup; then
      RESTART_OK=1
    fi
  fi

  if [[ "${RESTART_OK}" -eq 0 ]]; then
    if [[ "${ALLOW_RESTART_FAILURE:-0}" == "1" ]]; then
      echo "[WARN] Deploy concluído, mas a aplicação não foi reiniciada."
    else
      exit 1
    fi
  fi
fi

if command -v curl &>/dev/null; then
  HEALTH_URL="$(_ler_health_url)"
  echo "==> Healthcheck ${HEALTH_URL}..."
  HEALTH_RESPONSE=""
  for tentativa in $(seq 1 15); do
    HEALTH_RESPONSE="$(curl -fsS "${HEALTH_URL}" 2>/dev/null || true)"
    if [ -n "${HEALTH_RESPONSE}" ]; then
      break
    fi
    sleep 2
  done

  if [ -z "${HEALTH_RESPONSE}" ]; then
    echo "[ERRO] A aplicacao nao respondeu ao healthcheck apos o reinicio."
    exit 1
  fi

  echo "${HEALTH_RESPONSE}"
  if [[ -n "${DEPLOY_VERSION:-}" ]] && ! grep -Fq "\"deploy_version\":\"${DEPLOY_VERSION}\"" <<<"${HEALTH_RESPONSE}"; then
    echo "[ERRO] O healthcheck respondeu uma versao diferente do commit implantado."
    echo "       esperado: ${DEPLOY_VERSION}"
    exit 1
  fi
fi

echo "[OK] Deploy concluído."
