<?php
/**
 * BookiFlex Core
 *
 * @package    BookiFlex
 * @subpackage Core
 * @author     BookiFlex
 * @copyright  Copyright (c) 2025 BookiFlex
 * @license    GPL-2.0-or-later
 *
 * @link       https://bookiflex.com
 */

declare(strict_types=1);

namespace BookiFlex\Core\ORM;

use Cycle\Database\DatabaseManager;
use Cycle\Database\Schema\AbstractTable;
use Psr\Log\LoggerInterface;

defined( 'ABSPATH' ) || exit;

/**
 * Schema Manager - manages database schema creation and updates
 */
class SchemaManager
{
    private DatabaseManager $databaseManager;
    private array $schemaDefinitions = [];
    private ?LoggerInterface $logger;

    public function __construct(DatabaseManager $databaseManager, ?LoggerInterface $logger = null)
    {
        $this->databaseManager = $databaseManager;
        $this->logger = $logger;
    }

    /**
     * Register schema definition
     */
    public function registerSchema(string $schemaClass): void
    {
        if (!in_array($schemaClass, $this->schemaDefinitions)) {
            $this->schemaDefinitions[] = $schemaClass;
        }
    }

    /**
     * Create or update tables from schema definitions
     */
    public function createOrUpdateTables(array $schemaClasses): void
    {
        $database = $this->databaseManager->database();

        // Get all table definitions
        $tables = [];
        foreach ($schemaClasses as $schemaClass) {
            if (class_exists($schemaClass) && method_exists($schemaClass, 'define')) {
                $schema = new $schemaClass($database);
                $tableDef = $schema->define();

                if ($tableDef instanceof AbstractTable) {
                    $tables[] = $tableDef;
                }
            }
        }

        // Create tables without foreign keys first
        $database->getDriver()->beginTransaction();
        try {
            foreach ($tables as $table) {
                $table->save();
            }
            $database->getDriver()->commitTransaction();
        } catch (\Exception $e) {
            $this->logger?->error('SchemaManager table creation failed', ['error' => $e->getMessage()]);
            $database->getDriver()->rollbackTransaction();
            throw $e;
        }

        // Then add foreign keys
        $database->getDriver()->beginTransaction();
        try {
            foreach ($schemaClasses as $schemaClass) {
                if (method_exists($schemaClass, 'createForeignKeys')) {
                    $schema = new $schemaClass($database);
                    $schema->createForeignKeys();
                }
            }
            $database->getDriver()->commitTransaction();
        } catch (\Exception $e) {
            $database->getDriver()->rollbackTransaction();
            throw $e;
        }

        // Fix timestamps to have ON UPDATE CURRENT_TIMESTAMP
        $this->fixTimestamps($tables);
    }

    /**
     * Fix timestamp columns
     */
    private function fixTimestamps(array $tables): void
    {
        $fixer = new Schema\TimestampFixer($this->databaseManager->database());
        $fixer->addOnUpdateCurrentTimestamp($tables);
    }

    /**
     * Get all registered schemas
     */
    public function getRegisteredSchemas(): array
    {
        return $this->schemaDefinitions;
    }
}
