<?php

namespace CloudXM\SDK\Persistence;

use CloudXM\SDK\Support\HasLogger;
use CloudXM\SDK\Contracts\RepositoryInterface;

/**
 * Abstract Repository Base Class
 *
 * Provides common database operations for repository implementations.
 * Handles WordPress database interactions and provides a foundation
 * for concrete repository classes to extend.
 *
 * @package CloudXM\SDK\Persistence
 */
abstract class AbstractRepository implements RepositoryInterface
{
    use HasLogger;

    /**
     * WordPress database instance
     *
     * @var \wpdb
     */
    protected $wpdb;

    /**
     * Table prefix for this repository
     *
     * @var string
     */
    protected string $table_prefix;

    /**
     * Table name (without prefix)
     *
     * @var string
     */
    protected string $table_name;

    /**
     * Constructor
     *
     * @param string $table_prefix Plugin-specific table prefix (e.g., 'marlim_')
     * @param string $table_name Table name without prefix
     */
    public function __construct(string $table_prefix, string $table_name)
    {
        global $wpdb;
        $this->wpdb = $wpdb;
        $this->table_prefix = $table_prefix;
        $this->table_name = $table_name;
    }

    /**
     * Get full table name with prefix
     *
     * @return string
     */
    public function getTableName(): string
    {
        return $this->wpdb->prefix . $this->table_prefix . $this->table_name;
    }

    /**
     * Find entity by ID
     *
     * @param int $id
     * @return array|null
     */
    public function find(int $id): ?array
    {
        $table_name = $this->getTableName();

        $result = $this->wpdb->get_row(
            $this->wpdb->prepare(
                "SELECT * FROM {$table_name} WHERE id = %d",
                $id
            ),
            ARRAY_A
        );

        return $result ?: null;
    }

    /**
     * Find all entities with optional pagination and filtering
     *
     * @param array $conditions
     * @param int $limit
     * @param int $offset
     * @return array
     */
    public function findAll(array $conditions = [], int $limit = 50, int $offset = 0): array
    {
        $table_name = $this->getTableName();

        $where_clause = '';
        $values = [];

        if (!empty($conditions)) {
            $where_parts = [];
            foreach ($conditions as $column => $value) {
                $where_parts[] = "{$column} = %s";
                $values[] = $value;
            }
            $where_clause = 'WHERE ' . implode(' AND ', $where_parts);
        }

        $sql = "SELECT * FROM {$table_name} {$where_clause} LIMIT %d OFFSET %d";
        $values[] = $limit;
        $values[] = $offset;

        $results = $this->wpdb->get_results(
            $this->wpdb->prepare($sql, $values),
            ARRAY_A
        );

        return $results ?: [];
    }

    /**
     * Find single entity by conditions
     *
     * @param array $conditions
     * @return array|null
     */
    public function findOneBy(array $conditions): ?array
    {
        $table_name = $this->getTableName();

        $where_parts = [];
        $values = [];

        foreach ($conditions as $column => $value) {
            $where_parts[] = "{$column} = %s";
            $values[] = $value;
        }

        $where_clause = implode(' AND ', $where_parts);

        $result = $this->wpdb->get_row(
            $this->wpdb->prepare(
                "SELECT * FROM {$table_name} WHERE {$where_clause} LIMIT 1",
                $values
            ),
            ARRAY_A
        );

        return $result ?: null;
    }

    /**
     * Save new entity
     *
     * @param array $data
     * @return int
     */
    public function save(array $data): int
    {
        $table_name = $this->getTableName();

        $result = $this->wpdb->insert(
            $table_name,
            $data,
            $this->getFormatArray($data)
        );

        if ($result === false) {
            throw new \RuntimeException('Failed to save entity: ' . $this->wpdb->last_error);
        }

        return $this->wpdb->insert_id;
    }

    /**
     * Update existing entity
     *
     * @param int $id
     * @param array $data
     * @return bool
     */
    public function update(int $id, array $data): bool
    {
        $table_name = $this->getTableName();

        $result = $this->wpdb->update(
            $table_name,
            $data,
            ['id' => $id],
            $this->getFormatArray($data),
            ['%d']
        );

        return $result !== false;
    }

    /**
     * Delete entity by ID
     *
     * @param int $id
     * @return bool
     */
    public function delete(int $id): bool
    {
        $table_name = $this->getTableName();

        $result = $this->wpdb->delete(
            $table_name,
            ['id' => $id],
            ['%d']
        );

        return $result !== false;
    }

    /**
     * Count entities matching conditions
     *
     * @param array $conditions
     * @return int
     */
    public function count(array $conditions = []): int
    {
        $table_name = $this->getTableName();

        $where_clause = '';
        $values = [];

        if (!empty($conditions)) {
            $where_parts = [];
            foreach ($conditions as $column => $value) {
                $where_parts[] = "{$column} = %s";
                $values[] = $value;
            }
            $where_clause = 'WHERE ' . implode(' AND ', $where_parts);
        }

        $sql = "SELECT COUNT(*) FROM {$table_name} {$where_clause}";

        return (int) $this->wpdb->get_var(
            empty($values) ? $sql : $this->wpdb->prepare($sql, $values)
        );
    }

    /**
     * Get format array for wpdb operations
     *
     * @param array $data
     * @return array
     */
    private function getFormatArray(array $data): array
    {
        $formats = [];
        foreach ($data as $value) {
            if (is_int($value)) {
                $formats[] = '%d';
            } elseif (is_float($value)) {
                $formats[] = '%f';
            } else {
                $formats[] = '%s';
            }
        }
        return $formats;
    }
}