<?php
/**
 * Persistence layer for design-system index edges.
 *
 * @package LolaCoreForBricks
 */

declare(strict_types=1);

namespace LolaCoreForBricks\Index;

/**
 * Inserts and deletes rows in the index table.
 */
class IndexRepository
{
    /**
     * @param array<int, array<string, mixed>> $edges
     */
    public function insertEdges(array $edges): void
    {
        if ($edges === []) {
            return;
        }

        global $wpdb;
        $table = IndexSchema::tableName();

        foreach ($edges as $edge) {
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
            $wpdb->insert(
                $table,
                [
                    'ref_type'     => (string) ($edge['ref_type'] ?? ''),
                    'ref_id'       => (string) ($edge['ref_id'] ?? ''),
                    'post_id'      => (int) ($edge['post_id'] ?? 0),
                    'area'         => (string) ($edge['area'] ?? ''),
                    'element_id'   => isset($edge['element_id']) ? (string) $edge['element_id'] : null,
                    'element_name' => isset($edge['element_name']) ? (string) $edge['element_name'] : null,
                ],
                ['%s', '%s', '%d', '%s', '%s', '%s']
            );
        }
    }

    public function deleteForPost(int $postId): void
    {
        global $wpdb;
        $table = IndexSchema::tableName();

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->delete($table, ['post_id' => $postId], ['%d']);
    }

    public function deleteGlobalScope(): void
    {
        global $wpdb;
        $table = IndexSchema::tableName();

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->delete($table, ['post_id' => 0], ['%d']);
    }

    public function truncate(): void
    {
        global $wpdb;
        $table = IndexSchema::tableName();

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name uses $wpdb->prefix plus a fixed suffix.
        $wpdb->query("TRUNCATE TABLE {$table}");
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    public function findByRef(string $refType, string $refId): array
    {
        global $wpdb;

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $rows = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT ref_type, ref_id, post_id, area, element_id, element_name
                 FROM %i
                 WHERE ref_type = %s AND ref_id = %s
                 ORDER BY post_id ASC, area ASC",
                IndexSchema::tableName(),
                $refType,
                $refId
            ),
            ARRAY_A
        );

        return is_array($rows) ? $rows : [];
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    public function findByPost(int $postId): array
    {
        global $wpdb;

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $rows = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT ref_type, ref_id, post_id, area, element_id, element_name
                 FROM %i
                 WHERE post_id = %d
                 ORDER BY area ASC, element_id ASC",
                IndexSchema::tableName(),
                $postId
            ),
            ARRAY_A
        );

        return is_array($rows) ? $rows : [];
    }

    public function replaceRefId(string $refType, string $fromId, string $toId): void
    {
        if ($refType === '' || $fromId === '' || $toId === '' || $fromId === $toId) {
            return;
        }

        global $wpdb;

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->query(
            $wpdb->prepare(
                "UPDATE %i
                 SET ref_id = %s
                 WHERE ref_type = %s AND ref_id = %s",
                IndexSchema::tableName(),
                $toId,
                $refType,
                $fromId
            )
        );
    }

    /**
     * @param int[] $postIds
     */
    public function reindexPosts(array $postIds, ?IndexBuilder $builder = null): void
    {
        $builder = $builder ?? new IndexBuilder();

        foreach (array_unique(array_filter(array_map('intval', $postIds))) as $postId) {
            if ($postId <= 0) {
                continue;
            }

            $builder->indexPost($postId, $this);
        }
    }

    /**
     * @return string[]
     */
    public function distinctRefIds(string $refType): array
    {
        global $wpdb;

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $rows = $wpdb->get_col(
            $wpdb->prepare(
                "SELECT DISTINCT ref_id FROM %i WHERE ref_type = %s",
                IndexSchema::tableName(),
                $refType
            )
        );

        if (!is_array($rows)) {
            return [];
        }

        return array_values(array_map('strval', $rows));
    }
}
