#!/usr/bin/env bash
# ─────────────────────────────────────────────────────────────────────────────
# erp_module 검수 파이프라인 (CI 스타일) — setting → 계약 흐름
#
#  STAGE 1  정적 분석 (PHP lint + JS 구문)
#  STAGE 2  DB 스키마 무결성 (자리타입 2계층 + 정상가 컬럼)
#  STAGE 3  기능 스모크 (관리비/정상가 2계층 해석, 계약 도래 쿼리)
#  STAGE 4  HTTP 헬스 (주요 페이지/ API 응답)
#
#  사용:  bash bin/verify_setting_to_contract.sh
#  종료코드: 0 = 전부 통과, 1 = 실패 있음
# ─────────────────────────────────────────────────────────────────────────────
set -uo pipefail

ROOT="/home/erp_module/www"
DB="erp_module"
HOST="erp_module.acux.store"

PASS=0; FAIL=0; WARN=0
note()  { printf '   %s\n' "$1"; }
ok()    { printf '  \033[32m✓ PASS\033[0m  %s\n' "$1"; PASS=$((PASS+1)); }
bad()   { printf '  \033[31m✗ FAIL\033[0m  %s\n' "$1"; FAIL=$((FAIL+1)); }
warn()  { printf '  \033[33m! WARN\033[0m  %s\n' "$1"; WARN=$((WARN+1)); }
stage() { printf '\n\033[1m━━ %s\033[0m\n' "$1"; }

mysql_q() { mysql -N -u root "$DB" -e "$1" 2>/dev/null; }

# ── STAGE 1: 정적 분석 ───────────────────────────────────────────────────────
stage "STAGE 1 · 정적 분석 (PHP lint / JS 구문)"

lint_dir() {
  local dir="$1" label="$2" errs=0
  while IFS= read -r f; do
    if ! php -l "$f" >/dev/null 2>&1; then
      bad "PHP 구문 오류: ${f#$ROOT/}"; errs=$((errs+1))
    fi
  done < <(find "$dir" -name '*.php' 2>/dev/null)
  [ "$errs" -eq 0 ] && ok "$label PHP lint 통과 ($(find "$dir" -name '*.php' | wc -l | tr -d ' ')개)"
}
lint_dir "$ROOT/setting"               "setting"
lint_dir "$ROOT/admin/api/contract"    "contract API"
lint_dir "$ROOT/admin/pages/contract"  "contract 페이지"
lint_dir "$ROOT/_common"               "_common"

# JS 구문 (node 있으면)
if command -v node >/dev/null 2>&1; then
  js_err=0
  for f in \
    "$ROOT/setting/assets/js/pages/spot_tables_sub.js" \
    "$ROOT/admin/assets/js/pages/contract.js"; do
    [ -f "$f" ] || continue
    node -c "$f" >/dev/null 2>&1 || { bad "JS 구문 오류: ${f#$ROOT/}"; js_err=$((js_err+1)); }
  done
  [ "$js_err" -eq 0 ] && ok "JS 구문 검사 통과 (spot_tables_sub.js, contract.js)"
else
  warn "node 미설치 — JS 구문 검사 생략"
fi

# ── STAGE 2: DB 스키마 무결성 ────────────────────────────────────────────────
stage "STAGE 2 · DB 스키마 무결성 (자리타입 2계층 + 정상가)"

check_col() {
  local tbl="$1" col="$2"
  local c; c=$(mysql_q "SELECT COUNT(*) FROM information_schema.columns WHERE table_schema='$DB' AND table_name='$tbl' AND column_name='$col'")
  [ "$c" = "1" ] && ok "$tbl.$col 존재" || bad "$tbl.$col 누락"
}
check_col burial_category_spot_types maintenance_fee
check_col burial_category_spot_types list_price
check_col burial_spot_custom_types  maintenance_fee
check_col burial_spot_custom_types  list_price
check_col burial_spot_custom_types  source_type_id

# 2계층 통일 확인: 런타임 코드가 더 이상 burial_spot_fee_config 를 읽지 않아야 함
if grep -q "burial_spot_fee_config" "$ROOT/_common/functions.php"; then
  # 남은 참조가 주석/레거시 헬퍼인지, 런타임 COALESCE/JOIN 인지 구분
  if grep -nE "JOIN +burial_spot_fee_config|COALESCE\([^)]*bsfc" "$ROOT/_common/functions.php" >/dev/null; then
    bad "functions.php 런타임 쿼리에 burial_spot_fee_config 가 아직 사용됨 (2계층 통일 위반)"
  else
    ok "functions.php 런타임 해석에서 burial_spot_fee_config 제거됨 (잔여 참조는 비런타임)"
  fi
else
  ok "functions.php 에 burial_spot_fee_config 참조 없음"
fi

# 고아 자식 타입(존재하지 않는 spot 참조) 점검
ORPHAN=$(mysql_q "SELECT COUNT(*) FROM burial_spot_custom_types c LEFT JOIN burial_spots s ON s.id=c.spot_id WHERE c.deleted_at IS NULL AND s.id IS NULL")
[ "${ORPHAN:-0}" = "0" ] && ok "고아 자리타입(custom_types) 없음" || bad "고아 자리타입 ${ORPHAN}건 — 삭제된 자리를 참조"

# 셀 cell_type 정합성: sst_ 키가 실재 custom type 을 가리키는지
BADCELL=$(mysql_q "
  SELECT COUNT(*) FROM burial_spot_cells bsc
  WHERE bsc.cell_type LIKE 'sst\\_%'
    AND NOT EXISTS (SELECT 1 FROM burial_spot_custom_types c WHERE CONCAT('sst_',c.id)=bsc.cell_type AND c.deleted_at IS NULL)")
[ "${BADCELL:-0}" = "0" ] && ok "셀-자리타입(cell_type sst_) 참조 정합" || warn "끊어진 sst_ cell_type ${BADCELL}건 (삭제된 전용타입 참조)"

# ── STAGE 3: 기능 스모크 (PHP CLI) ──────────────────────────────────────────
stage "STAGE 3 · 기능 스모크 (관리비/정상가 2계층 해석)"

php -d display_errors=1 -r '
  $_SERVER["DOCUMENT_ROOT"] = "'"$ROOT"'";
  require "'"$ROOT"'/_common/db.php";
  require "'"$ROOT"'/_common/functions.php";
  $db = getDB();
  $fail = 0;

  // 함수 존재
  foreach (["getMaintenanceFee","getSpotTypeListPrice","getBurialSpotTypeMetaByCellType"] as $fn) {
    if (!function_exists($fn)) { echo "FUNC_MISSING:$fn\n"; $fail++; }
  }

  // 부모 자리타입 하나 골라 2계층 상속 검증 (임시 자식 생성→해석→정리)
  $parent = $db->query("SELECT id FROM burial_category_spot_types WHERE deleted_at IS NULL ORDER BY id LIMIT 1")->fetchColumn();
  $spot   = $db->query("SELECT id FROM burial_spots WHERE deleted_at IS NULL ORDER BY id LIMIT 1")->fetchColumn();
  if ($parent && $spot) {
    $db->prepare("UPDATE burial_category_spot_types SET maintenance_fee=5000, list_price=10000000 WHERE id=?")->execute([$parent]);
    // 자식(localize) 임시 생성
    $db->prepare("INSERT INTO burial_spot_custom_types (spot_id, source_type_id, name, cols, capacity, is_communal, maintenance_fee, list_price, bg_color, border_color, sort_order) VALUES (?,?,\"_qa_tmp\",1,1,0,7777,9999999,\"#fff\",\"#ccc\",0)")->execute([$spot,$parent]);
    $childId = (int)$db->lastInsertId();

    $pFee = getMaintenanceFee($spot, "st_$parent", $db);
    $cFee = getMaintenanceFee($spot, "sst_$childId", $db);
    $pList = getSpotTypeListPrice("st_$parent", $db);
    $cList = getSpotTypeListPrice("sst_$childId", $db);

    if ($pFee === 5000)      echo "OK:부모 관리비 상속(5000)\n";      else { echo "FAIL:부모 관리비=$pFee (기대 5000)\n"; $fail++; }
    if ($cFee === 7777)      echo "OK:자식 관리비 독립(7777)\n";      else { echo "FAIL:자식 관리비=$cFee (기대 7777)\n"; $fail++; }
    if ($pList === 10000000) echo "OK:부모 정상가 상속(10,000,000)\n"; else { echo "FAIL:부모 정상가=$pList\n"; $fail++; }
    if ($cList === 9999999)  echo "OK:자식 정상가 독립(9,999,999)\n";  else { echo "FAIL:자식 정상가=$cList\n"; $fail++; }

    // 정리
    $db->prepare("DELETE FROM burial_spot_custom_types WHERE id=?")->execute([$childId]);
    $db->prepare("UPDATE burial_category_spot_types SET list_price=0 WHERE id=?")->execute([$parent]);
  } else {
    echo "SKIP:부모 자리타입 또는 자리 없음 — 상속 검증 생략\n";
  }
  echo ($fail===0) ? "SMOKE_RESULT:PASS\n" : "SMOKE_RESULT:FAIL($fail)\n";
' 2>/tmp/qa_php_err.log | while IFS= read -r line; do
  case "$line" in
    OK:*)            ok "${line#OK:}" ;;
    FAIL:*)          bad "${line#FAIL:}" ;;
    SKIP:*)          warn "${line#SKIP:}" ;;
    FUNC_MISSING:*)  bad "함수 누락: ${line#FUNC_MISSING:}" ;;
    SMOKE_RESULT:*)  : ;;
    *)               note "$line" ;;
  esac
done
if [ -s /tmp/qa_php_err.log ]; then bad "기능 스모크 중 PHP 오류 발생:"; sed 's/^/      /' /tmp/qa_php_err.log; fi
rm -f /tmp/qa_php_err.log

# 계약 도래 쿼리(런타임 COALESCE 포함)가 오류 없이 실행되는지
php -r '
  $_SERVER["DOCUMENT_ROOT"]="'"$ROOT"'";
  require "'"$ROOT"'/_common/db.php"; require "'"$ROOT"'/_common/functions.php";
  $db=getDB();
  if (function_exists("getMaintenanceFeeList")) {
    try {
      // 런타임 COALESCE(자식→부모 2계층) 포함 쿼리 — 각 모드 실행
      foreach (["upcoming","all","arrears_year"] as $m) {
        $r = getMaintenanceFeeList($m, ["page"=>1,"per_page"=>1], $db);
        if (!is_array($r) || !array_key_exists("rows", $r)) { echo "QUERY_FAIL:mode=$m 응답 형식 이상\n"; exit; }
      }
      echo "QUERY_OK\n";
    } catch (Throwable $e){ echo "QUERY_FAIL:".$e->getMessage()."\n"; }
  } else { echo "QUERY_SKIP\n"; }
' 2>&1 | while IFS= read -r line; do
  case "$line" in
    QUERY_OK)    ok "계약 관리비 도래 쿼리 3모드 실행 OK (2계층 COALESCE 정상)" ;;
    QUERY_FAIL:*) bad "계약 도래 쿼리 오류: ${line#QUERY_FAIL:}" ;;
    QUERY_SKIP)  warn "getMaintenanceFeeList 함수 없음 — 쿼리 검증 생략" ;;
    *)           note "$line" ;;
  esac
done

# ── STAGE 4: HTTP 헬스 ──────────────────────────────────────────────────────
stage "STAGE 4 · HTTP 헬스 (주요 페이지/API 응답)"

http_code() { curl -s -o /dev/null -w '%{http_code}' --max-time 10 -H "Host: $HOST" "http://127.0.0.1$1" 2>/dev/null; }
http_check() {
  local path="$1" label="$2"; local code; code=$(http_code "$path")
  if [ "$code" = "500" ] || [ -z "$code" ] || [ "$code" = "000" ]; then
    bad "$label → HTTP ${code:-무응답} (서버 오류)"
  elif [ "$code" = "200" ] || [ "$code" = "302" ] || [ "$code" = "301" ] || [ "$code" = "401" ] || [ "$code" = "403" ]; then
    ok "$label → HTTP $code"
  else
    warn "$label → HTTP $code"
  fi
}
http_check "/setup-map-basic"   "setup-map-basic"
http_check "/setup-map-detail"  "setup-map-detail"
http_check "/setting/api/burial_category_spot_types.php" "자리타입(부모) API"
http_check "/admin/pages/contract/contract_detail.php?cell_id=1&spot_id=1&slot=1" "contract-detail"

# ── 요약 ────────────────────────────────────────────────────────────────────
printf '\n\033[1m━━ 결과 요약\033[0m\n'
printf '  통과 %d · 경고 %d · 실패 %d\n' "$PASS" "$WARN" "$FAIL"
[ "$FAIL" -eq 0 ] && { printf '  \033[32m전체 통과\033[0m\n'; exit 0; } || { printf '  \033[31m실패 항목 확인 필요\033[0m\n'; exit 1; }
