<?php

declare(strict_types=1);

namespace App\Framework\Console;

use App\Framework\Filesystem\ValueObjects\FilePath;

/**
 * Command history and favorites management for console TUI
 */
final class CommandHistory
{
    private array $history = [];

    private array $favorites = [];

    private int $maxHistorySize;

    private FilePath $historyFile;

    private FilePath $favoritesFile;

    public function __construct(
        int $maxHistorySize = 100,
        ?FilePath $storageDirectory = null
    ) {
        $this->maxHistorySize = $maxHistorySize;

        $storageDir = $storageDirectory ?? FilePath::create(sys_get_temp_dir() . '/console-history');
        $this->historyFile = $storageDir->join('command_history.json');
        $this->favoritesFile = $storageDir->join('command_favorites.json');

        $this->loadHistory();
        $this->loadFavorites();
    }

    /**
     * Add a command to history
     */
    public function addToHistory(string $commandName): void
    {
        // Remove if already exists to move to top
        $this->history = array_filter($this->history, fn ($entry) => $entry['command'] !== $commandName);

        // Add to beginning
        array_unshift($this->history, [
            'command' => $commandName,
            'timestamp' => time(),
            'count' => $this->getCommandCount($commandName) + 1,
        ]);

        // Limit history size
        if (count($this->history) > $this->maxHistorySize) {
            $this->history = array_slice($this->history, 0, $this->maxHistorySize);
        }

        $this->saveHistory();
    }

    /**
     * Add command to favorites
     */
    public function addToFavorites(string $commandName): void
    {
        if (! $this->isFavorite($commandName)) {
            $this->favorites[] = [
                'command' => $commandName,
                'added_at' => time(),
            ];
            $this->saveFavorites();
        }
    }

    /**
     * Remove command from favorites
     */
    public function removeFromFavorites(string $commandName): void
    {
        $this->favorites = array_filter(
            $this->favorites,
            fn ($entry) => $entry['command'] !== $commandName
        );
        $this->favorites = array_values($this->favorites); // Re-index
        $this->saveFavorites();
    }

    /**
     * Toggle favorite status
     */
    public function toggleFavorite(string $commandName): bool
    {
        if ($this->isFavorite($commandName)) {
            $this->removeFromFavorites($commandName);

            return false;
        } else {
            $this->addToFavorites($commandName);

            return true;
        }
    }

    /**
     * Check if command is in favorites
     */
    public function isFavorite(string $commandName): bool
    {
        return array_reduce(
            $this->favorites,
            fn ($carry, $entry) => $carry || $entry['command'] === $commandName,
            false
        );
    }

    /**
     * Get recent command history
     */
    public function getRecentHistory(int $limit = 10): array
    {
        return array_slice($this->history, 0, $limit);
    }

    /**
     * Get frequently used commands
     */
    public function getFrequentCommands(int $limit = 10): array
    {
        $commands = $this->history;

        // Sort by usage count (descending)
        usort($commands, fn ($a, $b) => $b['count'] <=> $a['count']);

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

    /**
     * Get all favorites
     */
    public function getFavorites(): array
    {
        return $this->favorites;
    }

    /**
     * Get command usage statistics
     */
    public function getCommandStats(string $commandName): array
    {
        $entry = array_find($this->history, fn ($entry) => $entry['command'] === $commandName);

        if (! $entry) {
            return [
                'command' => $commandName,
                'count' => 0,
                'last_used' => null,
                'is_favorite' => $this->isFavorite($commandName),
            ];
        }

        return [
            'command' => $commandName,
            'count' => $entry['count'],
            'last_used' => $entry['timestamp'],
            'is_favorite' => $this->isFavorite($commandName),
        ];
    }

    /**
     * Clear all history
     */
    public function clearHistory(): void
    {
        $this->history = [];
        $this->saveHistory();
    }

    /**
     * Clear all favorites
     */
    public function clearFavorites(): void
    {
        $this->favorites = [];
        $this->saveFavorites();
    }

    /**
     * Get suggestions based on partial input
     */
    public function getSuggestions(string $partial, int $limit = 5): array
    {
        $suggestions = [];

        // Add matching favorites first
        foreach ($this->favorites as $favorite) {
            if (str_starts_with($favorite['command'], $partial)) {
                $suggestions[] = [
                    'command' => $favorite['command'],
                    'type' => 'favorite',
                    'score' => 1000, // High priority for favorites
                ];
            }
        }

        // Add matching recent commands
        foreach ($this->history as $entry) {
            $command = $entry['command'];
            if (str_starts_with($command, $partial) && ! $this->isFavorite($command)) {
                $suggestions[] = [
                    'command' => $command,
                    'type' => 'recent',
                    'score' => 500 + $entry['count'], // Score based on usage count
                ];
            }
        }

        // Sort by score (descending) and limit
        usort($suggestions, fn ($a, $b) => $b['score'] <=> $a['score']);

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

    /**
     * Get command count from history
     */
    private function getCommandCount(string $commandName): int
    {
        $entry = array_find($this->history, fn ($entry) => $entry['command'] === $commandName);

        return $entry ? $entry['count'] : 0;
    }

    /**
     * Load history from file
     */
    private function loadHistory(): void
    {
        if ($this->historyFile->exists()) {
            $content = file_get_contents($this->historyFile->toString());
            $decoded = json_decode($content, true);
            if (is_array($decoded)) {
                $this->history = $decoded;
            }
        }
    }

    /**
     * Save history to file
     */
    private function saveHistory(): void
    {
        $this->ensureDirectoryExists($this->historyFile->getDirectory());
        file_put_contents(
            $this->historyFile->toString(),
            json_encode($this->history, JSON_PRETTY_PRINT)
        );
    }

    /**
     * Load favorites from file
     */
    private function loadFavorites(): void
    {
        if ($this->favoritesFile->exists()) {
            $content = file_get_contents($this->favoritesFile->toString());
            $decoded = json_decode($content, true);
            if (is_array($decoded)) {
                $this->favorites = $decoded;
            }
        }
    }

    /**
     * Save favorites to file
     */
    private function saveFavorites(): void
    {
        $this->ensureDirectoryExists($this->favoritesFile->getDirectory());
        file_put_contents(
            $this->favoritesFile->toString(),
            json_encode($this->favorites, JSON_PRETTY_PRINT)
        );
    }

    /**
     * Ensure storage directory exists
     */
    private function ensureDirectoryExists(FilePath $directory): void
    {
        if (! $directory->exists()) {
            mkdir($directory->toString(), 0755, true);
        }
    }
}
