<?php

declare(strict_types=1);

namespace App\Framework\Mcp\Tools;

use App\Framework\Database\ConnectionInterface;
use App\Framework\Database\ValueObjects\SqlQuery;
use App\Framework\Mcp\McpTool;

final readonly class DatabaseInspectionTools
{
    public function __construct(
        private ConnectionInterface $connection
    ) {
    }

    #[McpTool(
        name: 'inspect_database_tables',
        description: 'List all tables in the database with row counts and sizes'
    )]
    public function inspectTables(): array
    {
        $sql = "
            SELECT
                table_name,
                table_rows,
                ROUND(data_length / 1024 / 1024, 2) as size_mb,
                engine,
                table_collation
            FROM information_schema.tables
            WHERE table_schema = DATABASE()
            ORDER BY table_name
        ";

        $query = SqlQuery::create($sql, []);
        $result = $this->connection->query($query);
        $tables = $result->fetchAll();

        return [
            'database' => $this->getCurrentDatabase(),
            'table_count' => count($tables),
            'tables' => $tables,
        ];
    }

    #[McpTool(
        name: 'inspect_table_structure',
        description: 'Get detailed structure information for a specific table including columns, indexes, and foreign keys'
    )]
    public function inspectTableStructure(string $tableName): array
    {
        // Get columns
        $columnsSql = "
            SELECT
                column_name,
                column_type,
                is_nullable,
                column_default,
                column_key,
                extra
            FROM information_schema.columns
            WHERE table_schema = DATABASE()
            AND table_name = ?
            ORDER BY ordinal_position
        ";

        $columnsQuery = SqlQuery::create($columnsSql, [$tableName]);
        $columnsResult = $this->connection->query($columnsQuery);
        $columns = $columnsResult->fetchAll();

        // Get indexes
        $indexesSql = "
            SELECT
                index_name,
                column_name,
                non_unique,
                index_type
            FROM information_schema.statistics
            WHERE table_schema = DATABASE()
            AND table_name = ?
            ORDER BY index_name, seq_in_index
        ";

        $indexesQuery = SqlQuery::create($indexesSql, [$tableName]);
        $indexesResult = $this->connection->query($indexesQuery);
        $indexes = $indexesResult->fetchAll();

        // Get foreign keys
        $fkSql = "
            SELECT
                constraint_name,
                column_name,
                referenced_table_name,
                referenced_column_name
            FROM information_schema.key_column_usage
            WHERE table_schema = DATABASE()
            AND table_name = ?
            AND referenced_table_name IS NOT NULL
        ";

        $fkQuery = SqlQuery::create($fkSql, [$tableName]);
        $fkResult = $this->connection->query($fkQuery);
        $foreignKeys = $fkResult->fetchAll();

        return [
            'table_name' => $tableName,
            'columns' => $columns,
            'indexes' => $this->groupIndexes($indexes),
            'foreign_keys' => $foreignKeys,
        ];
    }

    #[McpTool(
        name: 'check_table_exists',
        description: 'Check if a table exists in the database'
    )]
    public function checkTableExists(string $tableName): array
    {
        $sql = "
            SELECT COUNT(*) as count
            FROM information_schema.tables
            WHERE table_schema = DATABASE()
            AND table_name = ?
        ";

        $query = SqlQuery::create($sql, [$tableName]);
        $result = $this->connection->query($query);
        $row = $result->fetch();

        $exists = ($row['count'] ?? 0) > 0;

        return [
            'table_name' => $tableName,
            'exists' => $exists,
            'database' => $this->getCurrentDatabase(),
        ];
    }

    #[McpTool(
        name: 'query_table_data',
        description: 'Query data from a table with optional limit and where conditions'
    )]
    public function queryTableData(
        string $tableName,
        int $limit = 10,
        ?string $whereColumn = null,
        ?string $whereValue = null
    ): array {
        $sql = "SELECT * FROM `{$tableName}`";
        $params = [];

        if ($whereColumn !== null && $whereValue !== null) {
            $sql .= " WHERE `{$whereColumn}` = ?";
            $params[] = $whereValue;
        }

        $sql .= " LIMIT ?";
        $params[] = $limit;

        $query = SqlQuery::create($sql, $params);
        $result = $this->connection->query($query);
        $rows = $result->fetchAll();

        return [
            'table_name' => $tableName,
            'row_count' => count($rows),
            'limit' => $limit,
            'where' => $whereColumn ? "{$whereColumn} = {$whereValue}" : null,
            'rows' => $rows,
        ];
    }

    #[McpTool(
        name: 'count_table_rows',
        description: 'Get exact row count for a table with optional where condition'
    )]
    public function countTableRows(
        string $tableName,
        ?string $whereColumn = null,
        ?string $whereValue = null
    ): array {
        $sql = "SELECT COUNT(*) as count FROM `{$tableName}`";
        $params = [];

        if ($whereColumn !== null && $whereValue !== null) {
            $sql .= " WHERE `{$whereColumn}` = ?";
            $params[] = $whereValue;
        }

        $query = SqlQuery::create($sql, $params);
        $result = $this->connection->query($query);
        $row = $result->fetch();

        return [
            'table_name' => $tableName,
            'row_count' => $row['count'] ?? 0,
            'where' => $whereColumn ? "{$whereColumn} = {$whereValue}" : null,
        ];
    }

    #[McpTool(
        name: 'find_foreign_key_references',
        description: 'Find all foreign key references to or from a specific table'
    )]
    public function findForeignKeyReferences(string $tableName): array
    {
        // References FROM this table
        $fromSql = "
            SELECT
                constraint_name,
                column_name,
                referenced_table_name,
                referenced_column_name,
                'outgoing' as direction
            FROM information_schema.key_column_usage
            WHERE table_schema = DATABASE()
            AND table_name = ?
            AND referenced_table_name IS NOT NULL
        ";

        $fromQuery = SqlQuery::create($fromSql, [$tableName]);
        $fromResult = $this->connection->query($fromQuery);
        $fromRefs = $fromResult->fetchAll();

        // References TO this table
        $toSql = "
            SELECT
                table_name as referencing_table,
                constraint_name,
                column_name as referencing_column,
                referenced_column_name,
                'incoming' as direction
            FROM information_schema.key_column_usage
            WHERE table_schema = DATABASE()
            AND referenced_table_name = ?
        ";

        $toQuery = SqlQuery::create($toSql, [$tableName]);
        $toResult = $this->connection->query($toQuery);
        $toRefs = $toResult->fetchAll();

        return [
            'table_name' => $tableName,
            'outgoing_references' => $fromRefs,
            'incoming_references' => $toRefs,
            'total_references' => count($fromRefs) + count($toRefs),
        ];
    }

    private function getCurrentDatabase(): string
    {
        $sql = "SELECT DATABASE() as db";
        $query = SqlQuery::create($sql, []);
        $result = $this->connection->query($query);
        $row = $result->fetch();

        return $row['db'] ?? 'unknown';
    }

    private function groupIndexes(array $indexes): array
    {
        $grouped = [];

        foreach ($indexes as $index) {
            $name = $index['index_name'];

            if (! isset($grouped[$name])) {
                $grouped[$name] = [
                    'index_name' => $name,
                    'type' => $index['index_type'],
                    'unique' => ! $index['non_unique'],
                    'columns' => [],
                ];
            }

            $grouped[$name]['columns'][] = $index['column_name'];
        }

        return array_values($grouped);
    }
}
