#!/usr/bin/env bash
set -euo pipefail

APP="/var/www/html/favo"
PATCH_DIR="$(cd "$(dirname "$0")/.." && pwd)"
TS="$(date +%Y%m%d_%H%M%S)"
BACKUP="$APP/storage/backups/reward_lifecycle_v1_1_auto_unlock_$TS"

echo "FAVO Reward Lifecycle V1.1 - Auto Unlock"
echo "APP: $APP"
echo "PATCH: $PATCH_DIR"

if [[ ! -f "$APP/app/Services/PurchaseService.php" ]]; then
  echo "ERROR: PurchaseService.php not found."
  exit 1
fi

if [[ ! -f "$APP/app/Services/UnlockService.php" ]]; then
  echo "ERROR: UnlockService.php not found."
  exit 1
fi

mkdir -p "$BACKUP"

cp -a "$APP/app/Services/PurchaseService.php" "$BACKUP/PurchaseService.php.before"
cp -a "$APP/app/Services/UnlockService.php" "$BACKUP/UnlockService.php.before"
cp -a "$APP/app/Controllers/TransactionController.php" "$BACKUP/TransactionController.php.before"

install -m 0644 "$PATCH_DIR/app/Services/AutoUnlockService.php" \
  "$APP/app/Services/AutoUnlockService.php"

# Apply narrowly-scoped source changes to the already-validated PurchaseService.
# The script refuses to continue if expected anchors are missing.
python3 - "$APP/app/Services/PurchaseService.php" <<'PY'
from pathlib import Path
import sys

p = Path(sys.argv[1])
s = p.read_text()

original = s

# 1) Add AutoUnlockService dependency import.
if "use Favo\\Services\\AutoUnlockService;" not in s:
    marker = "namespace Favo\\Services;\n\n"
    if marker not in s:
        raise SystemExit("ANCHOR ERROR: namespace marker not found")
    s = s.replace(marker, marker + "use Favo\\Services\\AutoUnlockService;\n\n", 1)

# 2) Add optional constructor dependency without disturbing existing args.
old = """        private ?LoyaltyService $loyalty = null
    ) {"""
new = """        private ?LoyaltyService $loyalty = null,
        private ?AutoUnlockService $autoUnlock = null
    ) {"""
if old in s:
    s = s.replace(old, new, 1)
elif "private ?AutoUnlockService $autoUnlock = null" not in s:
    raise SystemExit("ANCHOR ERROR: constructor signature not found")

# 3) Run unlock evaluation only after a real EARN, before commit.
anchor = """            $this->audit?->log(
                $accountId,
                'purchase.create',
                'purchase_transactions',
                $id,"""
if anchor not in s:
    raise SystemExit("ANCHOR ERROR: purchase audit block not found")

insert_before = """            $this->audit?->log(
                $accountId,
                'purchase.create',
                'purchase_transactions',
                $id,"""
replacement = """            // Auto Unlock V1.1: evaluate newly earned loyalty before COMMIT.
            // Idempotent retries return above and therefore cannot create duplicate unlocks.
            $autoUnlocked = [];
            if (
                $this->autoUnlock
                && ($points > 0 || $stamps > 0)
            ) {
                $autoUnlocked = $this->autoUnlock->evaluateAfterEarn(
                    $customerId,
                    $merchantId,
                    $programId > 0 ? $programId : null,
                    $accountId
                );
            }

            $this->audit?->log(
                $accountId,
                'purchase.create',
                'purchase_transactions',
                $id,"""
s = s.replace(insert_before, replacement, 1)

# 4) Expose auto-unlock data to controller without changing create() return type.
# Store it as last state.
prop_marker = "    private bool $lastIdempotent = false;\n"
if prop_marker in s and "private array $lastAutoUnlocked = [];" not in s:
    s = s.replace(prop_marker, prop_marker + "    private array $lastAutoUnlocked = [];\n", 1)

reset_marker = "        $this->lastIdempotent = false;\n"
if reset_marker in s:
    s = s.replace(reset_marker, reset_marker + "        $this->lastAutoUnlocked = [];\n", 1)

method_marker = """    public function wasIdempotent(): bool
    {
        return $this->lastIdempotent;
    }
"""
if method_marker in s and "function lastAutoUnlocked" not in s:
    s = s.replace(method_marker, method_marker + """
    public function lastAutoUnlocked(): array
    {
        return $this->lastAutoUnlocked;
    }
""", 1)

# Replace assignment so the response can report created unlocks.
s = s.replace(
"""                $autoUnlocked = $this->autoUnlock->evaluateAfterEarn(
                    $customerId,
                    $merchantId,
                    $programId > 0 ? $programId : null,
                    $accountId
                );""",
"""                $autoUnlocked = $this->autoUnlock->evaluateAfterEarn(
                    $customerId,
                    $merchantId,
                    $programId > 0 ? $programId : null,
                    $accountId
                );
                $this->lastAutoUnlocked = $autoUnlocked;""",
1
)

if s == original:
    raise SystemExit("NO CHANGE: PurchaseService was not modified")

p.write_text(s)
PY

# Controller gets the bridge injected and returns only newly-created unlocks.
python3 - "$APP/app/Controllers/TransactionController.php" <<'PY'
from pathlib import Path
import sys

p = Path(sys.argv[1])
s = p.read_text()
original = s

if "use Favo\\Services\\AutoUnlockService;" not in s:
    marker = "use Favo\\Services\\AuditService;\n"
    if marker not in s:
        raise SystemExit("ANCHOR ERROR: AuditService import not found")
    s = s.replace(marker, marker + "use Favo\\Services\\AutoUnlockService;\n", 1)

old = """        $loyalty = new LoyaltyService($db, $audit);
        $this->purchases = new PurchaseService($db, $audit, $loyalty);"""
new = """        $loyalty = new LoyaltyService($db, $audit);
        $autoUnlock = new AutoUnlockService($db);
        $this->purchases = new PurchaseService($db, $audit, $loyalty, $autoUnlock);"""
if old not in s:
    raise SystemExit("ANCHOR ERROR: PurchaseService construction not found")
s = s.replace(old, new, 1)

old_resp = """                'id' => $id,
                'idempotent' => $this->purchases->wasIdempotent(),"""
new_resp = """                'id' => $id,
                'idempotent' => $this->purchases->wasIdempotent(),
                'auto_unlocked' => $this->purchases->lastAutoUnlocked(),"""
if old_resp not in s:
    raise SystemExit("ANCHOR ERROR: transaction JSON response not found")
s = s.replace(old_resp, new_resp, 1)

if s == original:
    raise SystemExit("NO CHANGE: TransactionController was not modified")

p.write_text(s)
PY

php -l "$APP/app/Services/AutoUnlockService.php"
php -l "$APP/app/Services/PurchaseService.php"
php -l "$APP/app/Controllers/TransactionController.php"

echo
echo "Code deployment: PASS"
echo "Backup: $BACKUP"
echo "Auto Unlock is integrated before PurchaseService COMMIT."
echo "No database migration is required."
