<?php
/**
 * DatabaseTablesModule — central DB table registry.
 *
 * CHANGES FROM STUB VERSION
 *   • $table_map starts EMPTY — each feature module registers its own tables
 *     during its boot() via register_module_tables().
 *   • Infrastructure tables (security_logs, audit_logs, etc.) are registered
 *     by this module's own schema class in boot().
 *   • register_module_tables( array $map ) merges a module's table_map into
 *     the shared registry.
 *
 * @package UltraguardPro\modules\db_tables
 */

namespace UltraguardPro\modules\db_tables;

use UltraguardPro\modules\core\AbstractModule;
use UltraguardPro\contracts\ModuleSchemaInterface;

if ( ! defined( 'ABSPATH' ) ) { exit; }

// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange -- Database tables module intentionally coordinates schema discovery and install-time table work.

class DatabaseTablesModule extends AbstractModule {

    /** @var array<string, class-string>  short_key → FQCN, populated at runtime */
    private array $table_map = [];

    /** @var array<string, \UltraguardPro\db\BaseTable>  lazy-loaded instances */
    private array $instances = [];

    /** @var ModuleSchemaInterface[]  all registered module schemas */
    private array $registered_schemas = [];

    // ── ModuleInterface ───────────────────────────────────────────────────────

    public function get_id(): string { return 'db_tables'; }

    public function is_enabled(): bool { return true; }

    /**
     * Boot: register infrastructure tables and fire hooks for other modules.
     */
    public function boot(): void {
        $infra_schema = new schema\DbTablesSchema();
        $this->register_schema( $infra_schema );

        $this->table_map = apply_filters( 'ultraguard_pro/db_table_map', $this->table_map );

        do_action( 'ultraguard_pro/db_tables_registered', $this );
    }

    // ── Public API ─────────────────────────────────────────────────────────────

    /**
     * Register a module's table map into the shared registry.
     * Called by each feature module in its boot() method.
     *
     * @param  array<string, class-string> $map
     */
    public function register_module_tables( array $map ): void {
        $this->table_map = array_merge( $this->table_map, $map );
    }

    /**
     * Register a full module schema object (SQL + table map).
     */
    public function register_schema( ModuleSchemaInterface $schema ): void {
        $this->registered_schemas[ $schema->get_module_id() ] = $schema;
        $this->register_module_tables( $schema->get_table_map() );
    }

    /**
     * Return all registered schema objects (used by DatabaseInstaller).
     *
     * @return ModuleSchemaInterface[]
     */
    public function get_registered_schemas(): array {
        return $this->registered_schemas;
    }

    /**
     * Retrieve a table object by its short key. Lazy-loads on first access.
     */
    public function get_table( string $key ) {
        if ( isset( $this->instances[ $key ] ) ) {
            return $this->instances[ $key ];
        }

        if ( ! isset( $this->table_map[ $key ] ) ) {
            \UltraguardPro\core\Logger::error(
                "DB get_table[{$key}] key not in table_map.",
                [ 'available' => array_keys( $this->table_map ) ]
            );
            return null;
        }

        $class = $this->table_map[ $key ];
        if ( ! class_exists( $class ) ) {
            \UltraguardPro\core\Logger::error( "DB get_table[{$key}] class does not exist: {$class}" );
            return null;
        }

        try {
            $this->instances[ $key ] = new $class();
        } catch ( \Throwable $e ) {
            \UltraguardPro\core\Logger::error(
                "DB could not load table [{$key}] class [{$class}]: " . $e->getMessage(),
                [ 'file' => $e->getFile(), 'line' => $e->getLine() ]
            );
            return null;
        }

        return $this->instances[ $key ];
    }

    public function table_exists( string $key ): bool {
        return isset( $this->table_map[ $key ] ) && class_exists( $this->table_map[ $key ] );
    }

    public function is_table_ready( string $key ): bool {
        global $wpdb;
        $table = $this->get_table( $key );
        if ( ! $table ) { return false; }
        $tn = $wpdb->prefix . 'ultraguard_' . $key;
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery
        return (bool) $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $tn ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- query uses prepare(); table name is supplied as a value placeholder.
    }

    public function get_registered_keys(): array {
        return array_keys( $this->table_map );
    }

    public function get_all_tables(): array {
        foreach ( array_keys( $this->table_map ) as $key ) {
            $this->get_table( $key );
        }
        return $this->instances;
    }

    public function verify_tables( array $keys = [] ): array {
        $keys   = $keys ?: array_keys( $this->table_map );
        $result = [];
        foreach ( $keys as $key ) {
            $result[ $key ] = $this->is_table_ready( $key );
        }
        return [
            'all_ready' => ! in_array( false, $result, true ),
            'tables'    => $result,
        ];
    }
}
