<?php

declare(strict_types=1);

namespace App\Framework\StateManagement\Database;

use App\Framework\Core\ValueObjects\Timestamp;
use App\Framework\Database\EntityManager;
use App\Framework\Logging\Logger;
use App\Framework\StateManagement\StateHistoryManager;
use App\Framework\LiveComponents\Attributes\TrackStateHistory;

/**
 * Database-backed State History Manager
 *
 * Stores state change history in component_state_history table via EntityManager.
 * Integrates with #[TrackStateHistory] attribute for opt-in tracking.
 */
final class DatabaseStateHistoryManager implements StateHistoryManager
{
    private int $entriesAdded = 0;

    public function __construct(
        private readonly EntityManager $entityManager,
        private readonly ?Logger $logger = null,
    ) {}

    public function addHistoryEntry(
        string $componentId,
        string $stateData,
        string $stateClass,
        int $version,
        string $changeType,
        array $context = [],
        ?array $changedProperties = null,
        ?string $previousChecksum = null,
        string $currentChecksum = ''
    ): void {
        // Create history entry entity
        $entry = new StateHistoryEntry(
            id: 0, // Auto-increment will handle this
            componentId: $componentId,
            stateData: $stateData,
            stateClass: $stateClass,
            version: $version,
            changeType: StateChangeType::from($changeType),
            changedProperties: $changedProperties,
            userId: $context['user_id'] ?? null,
            sessionId: $context['session_id'] ?? null,
            ipAddress: $context['ip_address'] ?? null,
            userAgent: $context['user_agent'] ?? null,
            previousChecksum: $previousChecksum,
            currentChecksum: $currentChecksum,
            createdAt: Timestamp::now(),
        );

        // Persist via EntityManager
        $this->entityManager->unitOfWork->persist($entry);
        $this->entityManager->unitOfWork->commit();

        $this->entriesAdded++;
        $this->log('debug', "History entry added for component: {$componentId}, version: {$version}");
    }

    public function getHistory(string $componentId, int $limit = 100, int $offset = 0): array
    {
        // EntityManager findBy with criteria, orderBy, limit, offset
        $entries = $this->entityManager->findBy(
            StateHistoryEntry::class,
            ['component_id' => $componentId],
            ['created_at' => 'DESC'],
            $limit,
            $offset
        );

        $this->log('debug', "Retrieved {count} history entries for component: {$componentId}", [
            'count' => count($entries),
            'limit' => $limit,
            'offset' => $offset
        ]);

        return $entries;
    }

    public function getHistoryByVersion(string $componentId, int $version): ?StateHistoryEntry
    {
        $entries = $this->entityManager->findBy(
            StateHistoryEntry::class,
            [
                'component_id' => $componentId,
                'version' => $version
            ],
            limit: 1
        );

        return !empty($entries) ? $entries[0] : null;
    }

    public function getHistorySince(string $componentId, Timestamp $since, int $limit = 100): array
    {
        // Note: EntityManager findBy might need extension for timestamp comparison
        // For now, get all entries and filter in PHP
        $allEntries = $this->entityManager->findBy(
            StateHistoryEntry::class,
            ['component_id' => $componentId],
            ['created_at' => 'ASC']
        );

        $filtered = array_filter($allEntries, function (StateHistoryEntry $entry) use ($since) {
            return $entry->createdAt->isAfter($since) || $entry->createdAt->equals($since);
        });

        return array_slice($filtered, 0, $limit);
    }

    public function getHistoryByUser(string $userId, int $limit = 100): array
    {
        return $this->entityManager->findBy(
            StateHistoryEntry::class,
            ['user_id' => $userId],
            ['created_at' => 'DESC'],
            $limit
        );
    }

    public function cleanup(string $componentId, int $keepLast): int
    {
        // Get all entries for component ordered by created_at DESC
        $entries = $this->entityManager->findBy(
            StateHistoryEntry::class,
            ['component_id' => $componentId],
            ['created_at' => 'DESC']
        );

        // Keep first $keepLast entries, delete the rest
        $toDelete = array_slice($entries, $keepLast);
        $deletedCount = 0;

        foreach ($toDelete as $entry) {
            $this->entityManager->unitOfWork->remove($entry);
            $deletedCount++;
        }

        if ($deletedCount > 0) {
            $this->entityManager->unitOfWork->commit();
            $this->log('info', "Cleaned up {$deletedCount} history entries for component: {$componentId}");
        }

        return $deletedCount;
    }

    public function cleanupOlderThan(Timestamp $olderThan): int
    {
        // Get all entries (EntityManager limitation - need to filter in PHP)
        $allEntries = $this->entityManager->findBy(
            StateHistoryEntry::class,
            []
        );

        $toDelete = array_filter($allEntries, function (StateHistoryEntry $entry) use ($olderThan) {
            return $entry->createdAt->isBefore($olderThan);
        });

        $deletedCount = 0;
        foreach ($toDelete as $entry) {
            $this->entityManager->unitOfWork->remove($entry);
            $deletedCount++;
        }

        if ($deletedCount > 0) {
            $this->entityManager->unitOfWork->commit();
            $this->log('info', "Cleaned up {$deletedCount} history entries older than {$olderThan->format('Y-m-d H:i:s')}");
        }

        return $deletedCount;
    }

    public function deleteHistory(string $componentId): int
    {
        $entries = $this->entityManager->findBy(
            StateHistoryEntry::class,
            ['component_id' => $componentId]
        );

        $deletedCount = 0;
        foreach ($entries as $entry) {
            $this->entityManager->unitOfWork->remove($entry);
            $deletedCount++;
        }

        if ($deletedCount > 0) {
            $this->entityManager->unitOfWork->commit();
            $this->log('info', "Deleted all {$deletedCount} history entries for component: {$componentId}");
        }

        return $deletedCount;
    }

    public function isHistoryEnabled(string $componentClass): bool
    {
        // Check if class has #[TrackStateHistory] attribute
        if (!class_exists($componentClass)) {
            return false;
        }

        $reflection = new \ReflectionClass($componentClass);
        $attributes = $reflection->getAttributes(TrackStateHistory::class);

        return !empty($attributes);
    }

    public function getStatistics(): array
    {
        // Get all history entries
        $allEntries = $this->entityManager->findBy(
            StateHistoryEntry::class,
            []
        );

        if (empty($allEntries)) {
            return [
                'total_entries' => 0,
                'total_components' => 0,
                'oldest_entry' => null,
                'newest_entry' => null,
            ];
        }

        // Calculate statistics
        $componentIds = array_unique(array_map(
            fn(StateHistoryEntry $entry) => $entry->componentId,
            $allEntries
        ));

        $timestamps = array_map(
            fn(StateHistoryEntry $entry) => $entry->createdAt,
            $allEntries
        );

        usort($timestamps, fn(Timestamp $a, Timestamp $b) => $a->compareTo($b));

        return [
            'total_entries' => count($allEntries),
            'total_components' => count($componentIds),
            'oldest_entry' => $timestamps[0] ?? null,
            'newest_entry' => $timestamps[count($timestamps) - 1] ?? null,
        ];
    }

    /**
     * Log message if logger available
     */
    private function log(string $level, string $message, array $context = []): void
    {
        if ($this->logger === null) {
            return;
        }

        $context['entries_added'] = $this->entriesAdded;

        match ($level) {
            'debug' => $this->logger->debug($message, $context),
            'info' => $this->logger->info($message, $context),
            'warning' => $this->logger->warning($message, $context),
            'error' => $this->logger->error($message, $context),
            default => $this->logger->info($message, $context),
        };
    }
}
