<?php
// Shared walk access guard for walk-related APIs.
// Rule: staff can access all walks only with explicit global permission.
// Otherwise they can access only walks they created or where they are assigned
// as Manager, Head Walker, or Walker in walk_staff_assignments.

use App\Core\Auth;
use App\Core\Response;

if (!function_exists('pyrk_walk_access_table_exists')) {
    function pyrk_walk_access_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) {
            try {
                $stmt = $pdo->prepare('SHOW TABLES LIKE ?');
                $stmt->execute([$table]);
                return $cache[$table] = (bool)$stmt->fetchColumn();
            } catch (Throwable) {
                return $cache[$table] = false;
            }
        }
    }

    function pyrk_walk_access_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) {
            try {
                $stmt = $pdo->prepare('SHOW COLUMNS FROM `' . str_replace('`', '``', $table) . '` LIKE ?');
                $stmt->execute([$column]);
                return $cache[$key] = (bool)$stmt->fetchColumn();
            } catch (Throwable) {
                return $cache[$key] = false;
            }
        }
    }

    function pyrk_walk_access_view_all(?array $user): bool
    {
        if (!$user) return false;

        // View all walks must be explicit.
        // Event manager is NOT global by role anymore.
        return Auth::hasPermission('walks.manage', $user)
            || Auth::hasPermission('walks.view_all', $user)
            || in_array((string)($user['role_name'] ?? ''), ['super_admin'], true);
    }

    function pyrk_walk_access_view_assigned(?array $user): bool
    {
        if (!$user) return false;

        return pyrk_walk_access_view_all($user)
            || Auth::hasPermission('walks.view_assigned', $user)
            || Auth::hasPermission('walks.create', $user)
            || Auth::hasPermission('walks.edit', $user)
            || Auth::hasPermission('walks.assign_staff', $user)
            || Auth::hasPermission('attendance.manage', $user);
    }

    function pyrk_walk_access_user_id(?array $user): int
    {
        return (int)($user['id'] ?? 0);
    }

    function pyrk_walk_access_scope_sql(PDO $pdo, string $alias, ?array $user): string
    {
        $a = preg_replace('/[^a-zA-Z0-9_]/', '', $alias) ?: 'w';

        if (pyrk_walk_access_view_all($user)) {
            return '1=1';
        }

        $userId = pyrk_walk_access_user_id($user);
        if ($userId <= 0) {
            return '1=0';
        }

        $parts = [];

        if (pyrk_walk_access_column_exists($pdo, 'walks', 'created_by')) {
            $parts[] = $a . '.created_by = ' . $userId;
        }

        if (pyrk_walk_access_table_exists($pdo, 'walk_staff_assignments')) {
            $parts[] = 'EXISTS (
                SELECT 1
                FROM walk_staff_assignments wsa_access
                WHERE wsa_access.walk_id = ' . $a . '.id
                  AND wsa_access.user_id = ' . $userId . '
                  AND wsa_access.assignment_role IN ("manager", "head_walker", "walker")
            )';
        }

        if (!$parts) {
            return '1=0';
        }

        return '(' . implode(' OR ', $parts) . ')';
    }

    function pyrk_walk_access_can_view(PDO $pdo, int $walkId, ?array $user): bool
    {
        if ($walkId <= 0) return false;

        $sql = 'SELECT COUNT(*) FROM walks w WHERE w.id = ? AND ' . pyrk_walk_access_scope_sql($pdo, 'w', $user);
        $stmt = $pdo->prepare($sql);
        $stmt->execute([$walkId]);
        return (int)$stmt->fetchColumn() > 0;
    }

    function pyrk_walk_access_require(PDO $pdo, int $walkId, ?array $user, string $message = 'You are not assigned to this walk.'): void
    {
        if (!pyrk_walk_access_can_view($pdo, $walkId, $user)) {
            Response::error($message, 403);
        }
    }

    function pyrk_walk_access_assigned_role(PDO $pdo, int $walkId, ?array $user): ?string
    {
        $userId = pyrk_walk_access_user_id($user);
        if ($walkId <= 0 || $userId <= 0 || !pyrk_walk_access_table_exists($pdo, 'walk_staff_assignments')) return null;

        try {
            $stmt = $pdo->prepare('SELECT assignment_role FROM walk_staff_assignments WHERE walk_id = ? AND user_id = ? ORDER BY FIELD(assignment_role, "manager", "head_walker", "walker") LIMIT 1');
            $stmt->execute([$walkId, $userId]);
            $role = $stmt->fetchColumn();
            return $role ? (string)$role : null;
        } catch (Throwable) {
            return null;
        }
    }

    function pyrk_walk_access_is_creator(PDO $pdo, int $walkId, ?array $user): bool
    {
        $userId = pyrk_walk_access_user_id($user);
        if ($walkId <= 0 || $userId <= 0 || !pyrk_walk_access_column_exists($pdo, 'walks', 'created_by')) return false;

        try {
            $stmt = $pdo->prepare('SELECT COUNT(*) FROM walks WHERE id = ? AND created_by = ?');
            $stmt->execute([$walkId, $userId]);
            return (int)$stmt->fetchColumn() > 0;
        } catch (Throwable) {
            return false;
        }
    }

    function pyrk_walk_access_full_own_walk(PDO $pdo, int $walkId, ?array $user): bool
    {
        if (!$user || $walkId <= 0) return false;

        // Global full access is explicit only.
        if (pyrk_walk_access_view_all($user) || Auth::hasPermission('walks.manage', $user)) return true;

        // Creator and assigned Manager have full access only to this specific walk.
        return pyrk_walk_access_is_creator($pdo, $walkId, $user)
            || pyrk_walk_access_assigned_role($pdo, $walkId, $user) === 'manager';
    }

    function pyrk_walk_access_can_assign_staff(PDO $pdo, int $walkId, ?array $user): bool
    {
        if (!$user || $walkId <= 0) return false;

        // Global manager can assign staff everywhere.
        if (Auth::hasPermission('walks.manage', $user)) return true;

        // Explicit permission controls staff assignment, but it does not bypass walk visibility.
        if (Auth::hasPermission('walks.assign_staff', $user) && pyrk_walk_access_can_view($pdo, $walkId, $user)) return true;

        // The creator/assigned Manager can manage staff on his own walk.
        return pyrk_walk_access_full_own_walk($pdo, $walkId, $user);
    }

    function pyrk_walk_access_require_full_own_walk(PDO $pdo, int $walkId, ?array $user, string $message = 'You can manage only walks created by you or where you are the assigned manager.'): void
    {
        if (!pyrk_walk_access_full_own_walk($pdo, $walkId, $user)) {
            Response::error($message, 403);
        }
    }

    function pyrk_walk_access_require_assign_staff(PDO $pdo, int $walkId, ?array $user, string $message = 'You do not have permission to assign staff to this walk.'): void
    {
        if (!pyrk_walk_access_can_assign_staff($pdo, $walkId, $user)) {
            Response::error($message, 403);
        }
    }

}
