<?php

declare(strict_types=1);

namespace PpPhp\Core;

/**
 * Single source of truth for telling PLATFORM/APPLICATION-generated tables apart
 * from the USER's own business tables.
 *
 * Two ways a table is classified as "system":
 *   1. Its name is one of the known built-in platform tables (KNOWN) — these
 *      pre-date the naming convention, so they are listed here for back-compat.
 *   2. Its name starts with a reserved PREFIX (`pp_`, `ppphp_`, `system_`).
 *      => Every NEW platform table should be created with the `pp_` prefix so it
 *         is self-classifying and never needs to be added to KNOWN.
 *
 * This class is the authority for PHP; it is mirrored to the browser as
 * `window.PP_SYSTEM` by core/views/project-scripts.php so the JS SPAs share the
 * exact same rule. Do NOT reintroduce ad-hoc per-file lists — call this instead.
 */
final class SystemTables
{
    /** Reserved name prefixes — anything starting with these is system. */
    public const PREFIXES = ['pp_', 'ppphp_', 'system_'];

    /**
     * Built-in platform tables created without the prefix (legacy names). Keep
     * sorted. New tables should use the `pp_` prefix instead of being added here.
     */
    public const KNOWN = [
        'account_activations',
        'audit_log',
        'auth_attempts',
        'component_library',
        'field_encryption',
        'group_permissions',
        'groups',
        'notifications',
        'page_designs',
        'page_permissions',
        'password_resets',
        'project_versions',
        'projects',
        'record_locks',
        'remember_tokens',
        'row_security_rules',
        'security_audit',
        'settings',
        'social_auth_accounts',
        'user_2fa',
        'user_groups',
        'user_sessions',
        'users',
    ];

    /**
     * System tables that are still valid LOOKUP / foreign-key targets, so the
     * Designer's table pickers should offer them even though they're not
     * project tables — e.g. a field that references a user (assigned_to → users)
     * or a role (group_id → groups). They stay classified as system elsewhere
     * (no auto-generated CRUD pages).
     */
    public const REFERENCEABLE = ['users', 'groups'];

    /** @return string[] system tables that may be referenced from lookups/FKs. */
    public static function referenceable(): array
    {
        return self::REFERENCEABLE;
    }

    /** True when a table belongs to the platform (not a user's business table). */
    public static function isSystem(string $name): bool
    {
        $n = strtolower(trim($name));
        if ($n === '') return false;
        foreach (self::PREFIXES as $p) {
            if (str_starts_with($n, $p)) return true;
        }
        return in_array($n, self::KNOWN, true);
    }

    /** Inverse of isSystem — a user-created project/business table. */
    public static function isProject(string $name): bool
    {
        return !self::isSystem($name);
    }

    /** @param string[] $names @return string[] only the project/business tables */
    public static function projectOnly(array $names): array
    {
        return array_values(array_filter($names, static fn($n) => self::isProject((string) $n)));
    }

    /** @param string[] $names @return string[] only the platform/system tables */
    public static function systemOnly(array $names): array
    {
        return array_values(array_filter($names, static fn($n) => self::isSystem((string) $n)));
    }

    /** @return array{tables:string[],prefixes:string[]} shape for the JS mirror. */
    public static function forJs(): array
    {
        return ['tables' => self::KNOWN, 'prefixes' => self::PREFIXES];
    }
}
