<?php
require __DIR__ . '/bootstrap.php';

use App\Core\Auth;
use App\Core\Csrf;
use App\Core\Database;
use App\Core\Response;
use App\Core\Security;
use App\Services\TierService;

$user = Auth::requireUser();
$pdo = Database::connection();
$input = request_input();
if ($_POST) $input = array_merge($input, $_POST);
$method = $_SERVER['REQUEST_METHOD'];
$action = $input['action'] ?? 'store';

function pyrk_reward_can_manage(array $user): void
{
    if (!Auth::hasPermission('rewards.manage', $user) && !Auth::hasPermission('tiers.manage', $user) && !in_array($user['role_name'] ?? '', ['super_admin','event_manager'], true)) {
        Response::error('You do not have permission to manage prizes.', 403);
    }
}

function pyrk_reward_column_exists(PDO $pdo, string $table, string $column): bool
{
    static $cache = [];
    $key = $table . '.' . $column;
    if (array_key_exists($key, $cache)) return $cache[$key];
    try {
        $stmt = $pdo->prepare('SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ?');
        $stmt->execute([$table, $column]);
        return $cache[$key] = ((int)$stmt->fetchColumn() > 0);
    } catch (Throwable) { return $cache[$key] = false; }
}

function pyrk_reward_table_exists(PDO $pdo, string $table): bool
{
    static $cache = [];
    if (array_key_exists($table, $cache)) return $cache[$table];
    try {
        $stmt = $pdo->prepare('SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?');
        $stmt->execute([$table]);
        return $cache[$table] = ((int)$stmt->fetchColumn() > 0);
    } catch (Throwable) { return $cache[$table] = false; }
}

function pyrk_reward_try_schema_fix(PDO $pdo): void
{
    $alter = [];
    if (!pyrk_reward_column_exists($pdo, 'rewards', 'image_path')) $alter[] = 'ADD COLUMN image_path VARCHAR(255) NULL AFTER description';
    if (!pyrk_reward_column_exists($pdo, 'rewards', 'sponsor_id')) $alter[] = 'ADD COLUMN sponsor_id INT NULL AFTER image_path';
    if (!pyrk_reward_column_exists($pdo, 'rewards', 'redemption_instructions')) $alter[] = 'ADD COLUMN redemption_instructions TEXT NULL AFTER stock_quantity';
    if (!pyrk_reward_column_exists($pdo, 'rewards', 'available_from')) $alter[] = 'ADD COLUMN available_from DATE NULL AFTER redemption_instructions';
    if (!pyrk_reward_column_exists($pdo, 'rewards', 'available_until')) $alter[] = 'ADD COLUMN available_until DATE NULL AFTER available_from';
    if ($alter) { try { $pdo->exec('ALTER TABLE rewards ' . implode(', ', $alter)); } catch (Throwable) {} }

    if (pyrk_reward_table_exists($pdo, 'user_rewards')) {
        $userRewardAlter = [];
        foreach ([
            'points_spent' => 'ADD COLUMN points_spent INT NOT NULL DEFAULT 0 AFTER walk_id',
            'requested_at' => 'ADD COLUMN requested_at DATETIME NULL AFTER points_spent',
            'approved_by' => 'ADD COLUMN approved_by INT NULL AFTER requested_at',
            'approved_at' => 'ADD COLUMN approved_at DATETIME NULL AFTER approved_by',
            'fulfilled_by' => 'ADD COLUMN fulfilled_by INT NULL AFTER approved_at',
            'fulfilled_at' => 'ADD COLUMN fulfilled_at DATETIME NULL AFTER fulfilled_by',
            'rejected_by' => 'ADD COLUMN rejected_by INT NULL AFTER fulfilled_at',
            'rejected_at' => 'ADD COLUMN rejected_at DATETIME NULL AFTER rejected_by',
            'refunded_at' => 'ADD COLUMN refunded_at DATETIME NULL AFTER rejected_at',
            'fulfilment_notes' => 'ADD COLUMN fulfilment_notes TEXT NULL AFTER refunded_at',
        ] as $column => $sql) if (!pyrk_reward_column_exists($pdo, 'user_rewards', $column)) $userRewardAlter[] = $sql;
        if ($userRewardAlter) { try { $pdo->exec('ALTER TABLE user_rewards ' . implode(', ', $userRewardAlter)); } catch (Throwable) {} }
        try { $pdo->exec("ALTER TABLE user_rewards MODIFY status ENUM('requested','approved','fulfilled','rejected','cancelled','awarded','redeemed') NOT NULL DEFAULT 'requested'"); } catch (Throwable) {}
    }
    try { $pdo->exec("ALTER TABLE loyalty_transactions MODIFY transaction_type ENUM('walk_completed','badge_earned','photo_winner','manual_reward','adjustment','reward_claim','reward_refund') NOT NULL"); } catch (Throwable) {}
}

function pyrk_reward_ids(mixed $value): array
{
    if (is_string($value)) {
        $decoded = json_decode($value, true);
        $value = is_array($decoded) ? $decoded : preg_split('/\s*,\s*/', $value);
    }
    if (!is_array($value)) $value = [];
    return array_values(array_unique(array_filter(array_map('intval', $value))));
}

function pyrk_reward_input(array $input): array
{
    $name = trim((string)($input['reward_name'] ?? ''));
    if ($name === '') Response::error('Prize name is required.');
    $status = in_array(($input['status'] ?? 'active'), ['active','inactive'], true) ? $input['status'] : 'active';
    $from = trim((string)($input['available_from'] ?? ''));
    $until = trim((string)($input['available_until'] ?? ''));
    return [
        'reward_name' => $name,
        'description' => trim((string)($input['description'] ?? '')),
        'reward_type' => in_array(($input['reward_type'] ?? 'gift'), ['badge','points','gift','manual'], true) ? $input['reward_type'] : 'gift',
        'points_cost' => max(0, (int)($input['points_cost'] ?? 0)),
        'stock_quantity' => (($input['stock_quantity'] ?? '') === '') ? null : max(0, (int)$input['stock_quantity']),
        'redemption_instructions' => trim((string)($input['redemption_instructions'] ?? '')),
        'sponsor_id' => max(0, (int)($input['sponsor_id'] ?? 0)) ?: null,
        'available_from' => preg_match('/^\d{4}-\d{2}-\d{2}$/', $from) ? $from : null,
        'available_until' => preg_match('/^\d{4}-\d{2}-\d{2}$/', $until) ? $until : null,
        'tier_ids' => pyrk_reward_ids($input['tier_ids'] ?? []),
        'status' => $status,
    ];
}

function pyrk_reward_tier_ids(PDO $pdo, int $rewardId): array
{
    if (!pyrk_reward_table_exists($pdo, 'reward_tier_eligibility')) return [];
    $stmt = $pdo->prepare('SELECT tier_id FROM reward_tier_eligibility WHERE reward_id = ? ORDER BY tier_id');
    $stmt->execute([$rewardId]);
    return array_values(array_map('intval', $stmt->fetchAll(PDO::FETCH_COLUMN)));
}

function pyrk_reward_tier_names(PDO $pdo, array $tierIds): array
{
    if (!$tierIds || !pyrk_reward_table_exists($pdo, 'loyalty_tiers')) return [];
    $ph = implode(',', array_fill(0, count($tierIds), '?'));
    $stmt = $pdo->prepare("SELECT id, tier_name, icon FROM loyalty_tiers WHERE id IN ($ph) ORDER BY sort_order ASC, tier_name ASC");
    $stmt->execute($tierIds);
    return $stmt->fetchAll(PDO::FETCH_ASSOC);
}

function pyrk_reward_current_member_tier(PDO $pdo, int $userId): ?array
{
    try { return TierService::currentTier($pdo, $userId) ?: TierService::recalculateMember($pdo, $userId, 0); }
    catch (Throwable) { return null; }
}

function pyrk_reward_is_available_now(array $reward): bool
{
    $today = date('Y-m-d');
    if (!empty($reward['available_from']) && $reward['available_from'] > $today) return false;
    if (!empty($reward['available_until']) && $reward['available_until'] < $today) return false;
    return true;
}

function pyrk_reward_eligibility(PDO $pdo, int $userId, int $rewardId): array
{
    $tierIds = pyrk_reward_tier_ids($pdo, $rewardId);
    if (!$tierIds) return ['ok' => true, 'reason' => 'all_tiers', 'tier_ids' => [], 'current_tier' => null];
    $current = pyrk_reward_current_member_tier($pdo, $userId);
    if (!$current) return ['ok' => false, 'reason' => 'no_tier', 'tier_ids' => $tierIds, 'current_tier' => null];
    return ['ok' => in_array((int)$current['tier_id'], $tierIds, true), 'reason' => 'tier_limited', 'tier_ids' => $tierIds, 'current_tier' => $current];
}

function pyrk_reward_attach_enterprise(PDO $pdo, array $rewards): array
{
    foreach ($rewards as &$r) {
        $r['tier_ids'] = pyrk_reward_tier_ids($pdo, (int)$r['id']);
        $r['eligible_tiers'] = pyrk_reward_tier_names($pdo, $r['tier_ids']);
    }
    unset($r);
    return $rewards;
}

function pyrk_reward_sponsors(PDO $pdo): array
{
    if (!pyrk_reward_table_exists($pdo, 'reward_sponsors')) return [];
    return $pdo->query('SELECT * FROM reward_sponsors ORDER BY status ASC, sponsor_name ASC')->fetchAll(PDO::FETCH_ASSOC);
}

function pyrk_reward_tiers(PDO $pdo): array
{
    if (!pyrk_reward_table_exists($pdo, 'loyalty_tiers')) return [];
    return $pdo->query('SELECT * FROM loyalty_tiers ORDER BY status ASC, sort_order ASC, min_points ASC, tier_name ASC')->fetchAll(PDO::FETCH_ASSOC);
}

function pyrk_reward_save_eligibility(PDO $pdo, int $rewardId, array $tierIds): void
{
    if (!pyrk_reward_table_exists($pdo, 'reward_tier_eligibility')) return;
    $pdo->prepare('DELETE FROM reward_tier_eligibility WHERE reward_id = ?')->execute([$rewardId]);
    if (!$tierIds) return;
    $stmt = $pdo->prepare('INSERT IGNORE INTO reward_tier_eligibility (reward_id, tier_id, created_at) VALUES (?, ?, NOW())');
    foreach ($tierIds as $tid) $stmt->execute([$rewardId, $tid]);
}

pyrk_reward_try_schema_fix($pdo);

if ($method === 'GET' && $action === 'admin') {
    Auth::requireRole(['super_admin','event_manager','walk_leader']);
    $hasSponsor = pyrk_reward_column_exists($pdo, 'rewards', 'sponsor_id');
    $hasFrom = pyrk_reward_column_exists($pdo, 'rewards', 'available_from');
    $hasUntil = pyrk_reward_column_exists($pdo, 'rewards', 'available_until');
    $join = ($hasSponsor && pyrk_reward_table_exists($pdo, 'reward_sponsors')) ? ' LEFT JOIN reward_sponsors s ON s.id = r.sponsor_id' : '';
    $select = 'r.*'
        . ($hasSponsor ? ', s.sponsor_name' : ', NULL AS sponsor_name')
        . ($hasFrom ? '' : ', NULL AS available_from')
        . ($hasUntil ? '' : ', NULL AS available_until');
    $rewards = $pdo->query('SELECT ' . $select . ' FROM rewards r' . $join . ' ORDER BY r.status ASC, r.points_cost ASC, r.reward_name ASC')->fetchAll(PDO::FETCH_ASSOC);
    $rewards = pyrk_reward_attach_enterprise($pdo, $rewards);

    $claims = [];
    if (pyrk_reward_table_exists($pdo, 'user_rewards')) {
        $hasRequestedAt = pyrk_reward_column_exists($pdo, 'user_rewards', 'requested_at');
        $hasPointsSpent = pyrk_reward_column_exists($pdo, 'user_rewards', 'points_spent');
        $orderDate = $hasRequestedAt ? 'COALESCE(ur.requested_at, ur.created_at)' : 'ur.created_at';
        $pointsSql = $hasPointsSpent ? 'ur.points_spent' : 'rw.points_cost AS points_spent';
        $claimsSql = 'SELECT ur.*, ' . $pointsSql . ', rw.reward_name, rw.points_cost, rw.reward_type, u.full_name, u.email, u.mobile, u.profile_photo_path
            FROM user_rewards ur
            JOIN rewards rw ON rw.id = ur.reward_id
            JOIN users u ON u.id = ur.user_id
            ORDER BY FIELD(ur.status,"requested","approved","fulfilled","awarded","redeemed","rejected","cancelled"), ' . $orderDate . ' DESC, ur.id DESC
            LIMIT 400';
        try { $claims = $pdo->query($claimsSql)->fetchAll(PDO::FETCH_ASSOC); } catch (Throwable) { $claims = []; }
    }
    Response::ok(['rewards' => $rewards, 'claims' => $claims, 'tiers' => pyrk_reward_tiers($pdo), 'sponsors' => pyrk_reward_sponsors($pdo)]);
}

if ($method === 'GET' && $action === 'store') {
    $profileStmt = $pdo->prepare('SELECT COALESCE(loyalty_points,0) AS loyalty_points FROM member_profiles WHERE user_id = ? LIMIT 1');
    $profileStmt->execute([(int)$user['id']]);
    $points = (int)($profileStmt->fetchColumn() ?: 0);

    $hasSponsor = pyrk_reward_column_exists($pdo, 'rewards', 'sponsor_id');
    $join = ($hasSponsor && pyrk_reward_table_exists($pdo, 'reward_sponsors')) ? ' LEFT JOIN reward_sponsors s ON s.id = r.sponsor_id' : '';
    $select = 'r.*' . ($hasSponsor ? ', s.sponsor_name, s.logo_path AS sponsor_logo_path' : ', NULL AS sponsor_name, NULL AS sponsor_logo_path');
    $rows = $pdo->query('SELECT ' . $select . ' FROM rewards r' . $join . ' WHERE r.status = "active" ORDER BY r.points_cost ASC, r.reward_name ASC')->fetchAll(PDO::FETCH_ASSOC);
    $currentTier = pyrk_reward_current_member_tier($pdo, (int)$user['id']);
    $rewards = [];
    foreach (pyrk_reward_attach_enterprise($pdo, $rows) as $r) {
        if (!pyrk_reward_is_available_now($r)) continue;
        $eligibility = pyrk_reward_eligibility($pdo, (int)$user['id'], (int)$r['id']);
        if (!$eligibility['ok']) continue;
        $r['is_eligible'] = 1;
        $rewards[] = $r;
    }
    $claims = [];
    try {
        $claimsStmt = $pdo->prepare('SELECT ur.*, rw.reward_name, rw.description, rw.points_cost, rw.reward_type FROM user_rewards ur JOIN rewards rw ON rw.id = ur.reward_id WHERE ur.user_id = ? ORDER BY COALESCE(ur.requested_at, ur.created_at) DESC, ur.id DESC LIMIT 50');
        $claimsStmt->execute([(int)$user['id']]);
        $claims = $claimsStmt->fetchAll(PDO::FETCH_ASSOC);
    } catch (Throwable) {}
    Response::ok(['loyalty_points' => $points, 'current_tier' => $currentTier, 'rewards' => $rewards, 'claims' => $claims]);
}

if ($method === 'POST' && $action === 'save_reward') {
    pyrk_reward_can_manage($user);
    Csrf::validateFromRequest();
    $data = pyrk_reward_input($input);
    $id = (int)($input['id'] ?? 0);
    try {
        if ($id > 0) {
            $stmt = $pdo->prepare('UPDATE rewards SET reward_name = ?, description = ?, reward_type = ?, points_cost = ?, stock_quantity = ?, redemption_instructions = ?, sponsor_id = ?, available_from = ?, available_until = ?, status = ?, updated_at = NOW() WHERE id = ?');
            $stmt->execute([$data['reward_name'], $data['description'], $data['reward_type'], $data['points_cost'], $data['stock_quantity'], $data['redemption_instructions'], $data['sponsor_id'], $data['available_from'], $data['available_until'], $data['status'], $id]);
            pyrk_reward_save_eligibility($pdo, $id, $data['tier_ids']);
            Security::audit((int)$user['id'], 'reward_updated', 'rewards', $id, ['reward_name' => $data['reward_name'], 'tier_ids' => $data['tier_ids']]);
            Response::ok(['reward_id' => $id], 'Prize updated successfully.');
        }
        $stmt = $pdo->prepare('INSERT INTO rewards (reward_name, description, reward_type, points_cost, stock_quantity, redemption_instructions, sponsor_id, available_from, available_until, status, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())');
        $stmt->execute([$data['reward_name'], $data['description'], $data['reward_type'], $data['points_cost'], $data['stock_quantity'], $data['redemption_instructions'], $data['sponsor_id'], $data['available_from'], $data['available_until'], $data['status']]);
        $newId = (int)$pdo->lastInsertId();
        pyrk_reward_save_eligibility($pdo, $newId, $data['tier_ids']);
        Security::audit((int)$user['id'], 'reward_created', 'rewards', $newId, ['reward_name' => $data['reward_name'], 'tier_ids' => $data['tier_ids']]);
        Response::ok(['reward_id' => $newId], 'Prize created successfully.');
    } catch (Throwable $e) {
        Response::error('Could not save prize: ' . $e->getMessage(), 500);
    }
}

if ($method === 'POST' && $action === 'claim') {
    Csrf::validateFromRequest();
    $rewardId = (int)($input['reward_id'] ?? 0);
    if (!$rewardId) Response::error('Missing prize id.');
    $pdo->beginTransaction();
    try {
        $stmt = $pdo->prepare('SELECT * FROM rewards WHERE id = ? AND status = "active" LIMIT 1 FOR UPDATE');
        $stmt->execute([$rewardId]);
        $reward = $stmt->fetch(PDO::FETCH_ASSOC);
        if (!$reward) throw new RuntimeException('Prize is not available.');
        if (!pyrk_reward_is_available_now($reward)) throw new RuntimeException('This prize is not available during the current date window.');
        $eligibility = pyrk_reward_eligibility($pdo, (int)$user['id'], $rewardId);
        if (!$eligibility['ok']) throw new RuntimeException('This prize is open only for specific loyalty tiers.');
        if ($reward['stock_quantity'] !== null && (int)$reward['stock_quantity'] <= 0) throw new RuntimeException('This prize is out of stock.');
        $profile = $pdo->prepare('SELECT loyalty_points FROM member_profiles WHERE user_id = ? FOR UPDATE');
        $profile->execute([(int)$user['id']]);
        $available = (int)($profile->fetchColumn() ?: 0);
        $cost = (int)$reward['points_cost'];
        if ($available < $cost) throw new RuntimeException('You do not have enough points for this prize.');
        $pdo->prepare('UPDATE member_profiles SET loyalty_points = loyalty_points - ?, updated_at = NOW() WHERE user_id = ?')->execute([$cost, (int)$user['id']]);
        if ($reward['stock_quantity'] !== null) {
            $pdo->prepare('UPDATE rewards SET stock_quantity = GREATEST(0, stock_quantity - 1), updated_at = NOW() WHERE id = ?')->execute([$rewardId]);
        }
        $claim = $pdo->prepare('INSERT INTO user_rewards (user_id, reward_id, points_spent, status, requested_at, awarded_at, created_at) VALUES (?, ?, ?, "requested", NOW(), NOW(), NOW())');
        $claim->execute([(int)$user['id'], $rewardId, $cost]);
        $claimId = (int)$pdo->lastInsertId();
        $pdo->prepare('INSERT INTO loyalty_transactions (user_id, walk_id, points, transaction_type, description, created_at) VALUES (?, NULL, ?, "reward_claim", ?, NOW())')->execute([(int)$user['id'], -$cost, 'Claimed prize: ' . $reward['reward_name']]);
        try { TierService::recalculateMember($pdo, (int)$user['id'], 0); } catch (Throwable) {}
        $pdo->commit();
        Response::ok(['claim_id' => $claimId], 'Prize claim sent. Your points were deducted and the team will prepare your prize.');
    } catch (Throwable $e) {
        if ($pdo->inTransaction()) $pdo->rollBack();
        Response::error($e->getMessage(), 400);
    }
}

if ($method === 'POST' && $action === 'update_claim') {
    pyrk_reward_can_manage($user);
    Csrf::validateFromRequest();
    $claimId = (int)($input['claim_id'] ?? 0);
    $newStatus = (string)($input['status'] ?? '');
    if (!$claimId || !in_array($newStatus, ['approved','fulfilled','rejected','cancelled'], true)) Response::error('Invalid claim update.');
    $notes = trim((string)($input['fulfilment_notes'] ?? ''));
    $pdo->beginTransaction();
    try {
        $stmt = $pdo->prepare('SELECT ur.*, rw.reward_name FROM user_rewards ur JOIN rewards rw ON rw.id = ur.reward_id WHERE ur.id = ? LIMIT 1 FOR UPDATE');
        $stmt->execute([$claimId]);
        $claim = $stmt->fetch(PDO::FETCH_ASSOC);
        if (!$claim) throw new RuntimeException('Claim not found.');
        if (in_array($claim['status'], ['fulfilled','rejected','cancelled'], true)) throw new RuntimeException('This claim is already closed.');
        if (in_array($newStatus, ['rejected','cancelled'], true) && empty($claim['refunded_at'])) {
            $pdo->prepare('UPDATE member_profiles SET loyalty_points = loyalty_points + ?, updated_at = NOW() WHERE user_id = ?')->execute([(int)($claim['points_spent'] ?? 0), (int)$claim['user_id']]);
            $pdo->prepare('INSERT INTO loyalty_transactions (user_id, walk_id, points, transaction_type, description, created_at) VALUES (?, NULL, ?, "reward_refund", ?, NOW())')->execute([(int)$claim['user_id'], (int)($claim['points_spent'] ?? 0), 'Refunded prize claim: ' . $claim['reward_name']]);
            $pdo->prepare('UPDATE rewards SET stock_quantity = IF(stock_quantity IS NULL, NULL, stock_quantity + 1), updated_at = NOW() WHERE id = ?')->execute([(int)$claim['reward_id']]);
            try { TierService::recalculateMember($pdo, (int)$claim['user_id'], (int)$user['id']); } catch (Throwable) {}
        }
        if ($newStatus === 'approved') {
            $sql = 'UPDATE user_rewards SET status = "approved", approved_by = ?, approved_at = NOW(), fulfilment_notes = ?, updated_at = NOW() WHERE id = ?';
        } elseif ($newStatus === 'fulfilled') {
            $sql = 'UPDATE user_rewards SET status = "fulfilled", fulfilled_by = ?, fulfilled_at = NOW(), redeemed_at = NOW(), fulfilment_notes = ?, updated_at = NOW() WHERE id = ?';
        } elseif ($newStatus === 'rejected') {
            $sql = 'UPDATE user_rewards SET status = "rejected", rejected_by = ?, rejected_at = NOW(), refunded_at = IFNULL(refunded_at, NOW()), fulfilment_notes = ?, updated_at = NOW() WHERE id = ?';
        } else {
            $sql = 'UPDATE user_rewards SET status = "cancelled", rejected_by = ?, rejected_at = NOW(), refunded_at = IFNULL(refunded_at, NOW()), fulfilment_notes = ?, updated_at = NOW() WHERE id = ?';
        }
        $pdo->prepare($sql)->execute([(int)$user['id'], $notes, $claimId]);
        Security::audit((int)$user['id'], 'reward_claim_' . $newStatus, 'user_rewards', $claimId, ['status' => $newStatus]);
        $pdo->commit();
        Response::ok([], 'Claim updated.');
    } catch (Throwable $e) {
        if ($pdo->inTransaction()) $pdo->rollBack();
        Response::error($e->getMessage(), 500);
    }
}

Response::error('Unsupported rewards action.', 404);
