#!/usr/bin/env bash
# =============================================================================
# Generic University — 一鍵環境修復 + 啟動腳本
# 目標: InsiderPhD/Generic-University (Laravel 7 API 靶機)
#
# 這支腳本會:
#   0. 若當前不在 repo 內,則自動 git clone
#   1. 寫入釘死 PHP 8.0 的 Docker stack (php:8.0-apache + mariadb:10.6)
#   2~4. 套用讓 seed 能跑完的三個原始碼修復
#   5. (選配) 修好 admin delete 端點的外鍵 bug —— 預設「關」
#   6. down -v 後 up --build,等 seed 完成
#
# 修復對照 (詳見 writeup):
#   Fix1  composer.lock 缺 graphql-laravel  -> entrypoint 用 `composer update`
#   Fix2  DatabaseSeeder 的 5 個 seeder 被註解 -> 全部解註解
#   Fix3  UserSeeder 只建 7 個 user, ExtraGrades 需要 id 9 -> student 迴圈 5->10
#   Fix4  ExtraGrades 重複 enroll user 9 -> 移除多餘那行 (pivot PK 衝突)
#   Fix5  admin delete 用錯順序 truncate 撞外鍵 -> 關 FK check 再刪 (選配)
#
# 用法:
#   bash setup.sh                 # 套 Fix1~4 並啟動 (靶機原味, delete 會噴 FK error)
#   APPLY_DELETE_FIX=1 bash setup.sh   # 額外套 Fix5, 讓 delete/restore 端點能跑完
# =============================================================================
set -euo pipefail

REPO_URL="https://github.com/InsiderPhD/Generic-University.git"
DIR="Generic-University"
APPLY_DELETE_FIX="${APPLY_DELETE_FIX:-0}"

# --- 0. 取得程式碼 -----------------------------------------------------------
if [ ! -f artisan ]; then
  if [ ! -d "$DIR" ]; then
    echo "[*] Cloning $REPO_URL"
    git clone "$REPO_URL" "$DIR"
  fi
  cd "$DIR"
fi
echo "[*] Working dir: $(pwd)"

# --- 1. Docker stack ---------------------------------------------------------
echo "[*] Writing Docker files (PHP pinned to 8.0)"
cat > Dockerfile <<'DOCKERFILE'
FROM php:8.0-apache
RUN apt-get update && apt-get install -y --no-install-recommends \
        git unzip libonig-dev libzip-dev default-mysql-client \
    && docker-php-ext-install pdo_mysql mbstring bcmath zip \
    && a2enmod rewrite \
    && rm -rf /var/lib/apt/lists/*
ENV APACHE_DOCUMENT_ROOT=/var/www/html/public
RUN sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf \
    && sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
WORKDIR /var/www/html
COPY docker-entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
CMD ["apache2-foreground"]
DOCKERFILE

cat > docker-entrypoint.sh <<'ENTRY'
#!/bin/bash
set -e
cd /var/www/html
git config --global --add safe.directory /var/www/html
[ -f .env ] || cp .env.example .env
sed -i \
  -e 's/^DB_HOST=.*/DB_HOST=db/' \
  -e 's/^DB_DATABASE=.*/DB_DATABASE=generic/' \
  -e 's/^DB_USERNAME=.*/DB_USERNAME=uni/' \
  -e 's/^DB_PASSWORD=.*/DB_PASSWORD=password/' \
  .env
# Fix1: lock 檔缺 graphql-laravel, 用 update (容器內 PHP 已釘 8.0, 安全)
[ -d vendor ] || composer update --no-interaction --no-progress
grep -q '^APP_KEY=base64:' .env || php artisan key:generate --force
chown -R www-data:www-data storage bootstrap/cache
chmod -R 775 storage bootstrap/cache
echo "Waiting for database..."
until php -r "new PDO('mysql:host=db;port=3306','uni','password');" >/dev/null 2>&1; do sleep 3; done
if [ ! -f storage/.seeded ]; then
  php artisan migrate --force
  php artisan db:seed --force
  touch storage/.seeded
fi
exec "$@"
ENTRY

cat > docker-compose.yml <<'COMPOSE'
services:
  app:
    build: .
    ports:
      - "8000:80"
    volumes:
      - .:/var/www/html
    depends_on:
      - db
  db:
    image: mariadb:10.6
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: generic
      MYSQL_USER: uni
      MYSQL_PASSWORD: password
    volumes:
      - dbdata:/var/lib/mysql
volumes:
  dbdata:
COMPOSE

# --- 2. Fix2: 解註解全部 seeder ---------------------------------------------
echo "[*] Fix2: enabling all seeders"
cat > database/seeds/DatabaseSeeder.php <<'PHP'
<?php

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    public function run()
    {
        $this->call(RoleSeeder::class);
        $this->call(UserSeeder::class);
        $this->call(UniClassSeeder::class);
        $this->call(GradesSeeder::class);
        $this->call(VulnerabilitySeeder::class);
        $this->call(ExtraGradesTableSeeder::class);
    }
}
PHP

# --- 3. Fix3: UserSeeder 5 -> 10 個 student ---------------------------------
echo "[*] Fix3: bumping student count 5 -> 10"
sed -i 's/\$i < 5;/\$i < 10;/' database/seeds/UserSeeder.php

# --- 4. Fix4: ExtraGrades 移除多餘 enroll (pivot PK 衝突) --------------------
echo "[*] Fix4: fixing ExtraGrades PK collision"
cat > database/seeds/ExtraGradesTableSeeder.php <<'PHP'
<?php

use Illuminate\Database\Seeder;

class ExtraGradesTableSeeder extends Seeder
{
    public function run()
    {
        $user = \App\User::findOrFail(9);
        // user 9 is already enrolled in every class via UniClassSeeder,
        // so just add an extra grade per class (no re-enrollment -> no PK clash)
        foreach (\App\UniClass::all() as $class) {
            $percent = rand(0, 80);
            $grade = new \App\Grade();
            $grade->grade = $percent;
            $grade->comments = "Good job!";
            $grade->user()->associate($user);
            $grade->uniClass()->associate($class);
            $grade->save();
        }
    }
}
PHP

# --- 5. Fix5 (選配): 修 admin delete 的外鍵 truncate bug --------------------
if [ "$APPLY_DELETE_FIX" = "1" ]; then
  echo "[*] Fix5: patching AdminController delete() to be FK-safe"
  python3 - <<'PY'
import re, io
p = "app/Http/Controllers/AdminController.php"
s = open(p, encoding="utf-8").read()
old = """            DB::table('users')->where('id', '<>', Auth::user()->id)->delete();
            UniClass::truncate();
            Grade::truncate();"""
new = """            DB::statement('SET FOREIGN_KEY_CHECKS=0');
            Grade::truncate();
            UniClass::truncate();
            DB::table('uni_class_user')->truncate();
            DB::table('users')->where('id', '<>', Auth::user()->id)->delete();
            DB::statement('SET FOREIGN_KEY_CHECKS=1');"""
if old in s:
    open(p, "w", encoding="utf-8").write(s.replace(old, new))
    print("    [+] delete() patched")
elif "SET FOREIGN_KEY_CHECKS=0" in s:
    print("    [=] already patched, skipping")
else:
    print("    [!] delete() block not found — skipping (source may have changed)")
PY
else
  echo "[*] Fix5 skipped (delete endpoint will throw the FK error — that is objective #14's finding)."
  echo "    Re-run with:  APPLY_DELETE_FIX=1 bash setup.sh   to make delete/restore work end-to-end."
fi

# --- 6. 啟動 -----------------------------------------------------------------
echo "[*] Bringing up the stack (down -v first for a clean seed)"
docker compose down -v 2>/dev/null || true
docker compose up -d --build

echo "[*] Waiting for seeding to finish (up to ~4 min on first build)..."
deadline=$(( $(date +%s) + 300 ))
until docker compose logs app 2>/dev/null | grep -q "Database seeding completed successfully"; do
  if [ "$(date +%s)" -ge "$deadline" ]; then
    echo "[!] Timed out waiting for seed. Check: docker compose logs -f app"
    exit 1
  fi
  sleep 5
done

echo
echo "[+] Done — Generic University is up at http://localhost:8000"
echo "    Sanity check: curl -s http://localhost:8000/api/users | head -c 120"
echo "    Reset anytime: docker compose down -v && docker compose up -d"
