<?php

namespace App\Core;

abstract class Model
{
    protected Database $db;
    protected string $table = '';
    protected string $primaryKey = 'id';

    public function __construct()
    {
        $this->db = Database::getInstance();
    }

    public function find(int $id): ?array
    {
        $sql = "SELECT * FROM {$this->table} WHERE {$this->primaryKey} = ?";
        return $this->db->queryOne($sql, [$id]);
    }

    public function all(?string $orderBy = null): array
    {
        $sql = "SELECT * FROM {$this->table}";
        if ($orderBy) {
            $sql .= " ORDER BY {$orderBy}";
        }
        return $this->db->query($sql);
    }

    public function where(array $conditions, ?string $orderBy = null): array
    {
        $where = [];
        $params = [];
        
        foreach ($conditions as $column => $value) {
            $where[] = "{$column} = ?";
            $params[] = $value;
        }
        
        $sql = "SELECT * FROM {$this->table} WHERE " . implode(' AND ', $where);
        
        if ($orderBy) {
            $sql .= " ORDER BY {$orderBy}";
        }
        
        return $this->db->query($sql, $params);
    }

    public function findWhere(array $conditions): ?array
    {
        $where = [];
        $params = [];
        
        foreach ($conditions as $column => $value) {
            $where[] = "{$column} = ?";
            $params[] = $value;
        }
        
        $sql = "SELECT * FROM {$this->table} WHERE " . implode(' AND ', $where) . " LIMIT 1";
        return $this->db->queryOne($sql, $params);
    }

    public function create(array $data): ?int
    {
        $columns = array_keys($data);
        $placeholders = array_fill(0, count($data), '?');
        
        $sql = sprintf(
            "INSERT INTO %s (%s) VALUES (%s) RETURNING %s",
            $this->table,
            implode(', ', $columns),
            implode(', ', $placeholders),
            $this->primaryKey
        );
        
        $stmt = $this->db->getConnection()->prepare($sql);
        
        // Правильно биндим параметры с учетом типов
        $params = array_values($data);
        foreach ($params as $index => $param) {
            $paramNum = $index + 1;
            if (is_bool($param)) {
                $stmt->bindValue($paramNum, $param, \PDO::PARAM_BOOL);
            } elseif (is_int($param)) {
                $stmt->bindValue($paramNum, $param, \PDO::PARAM_INT);
            } elseif (is_null($param)) {
                $stmt->bindValue($paramNum, $param, \PDO::PARAM_NULL);
            } else {
                $stmt->bindValue($paramNum, $param, \PDO::PARAM_STR);
            }
        }
        
        $stmt->execute();
        
        return (int) $stmt->fetchColumn();
    }

    public function update(int $id, array $data): bool
    {
        $set = [];
        $params = [];
        
        foreach ($data as $column => $value) {
            $set[] = "{$column} = ?";
            $params[] = $value;
        }
        $params[] = $id;
        
        $sql = sprintf(
            "UPDATE %s SET %s WHERE %s = ?",
            $this->table,
            implode(', ', $set),
            $this->primaryKey
        );
        
        return $this->db->execute($sql, $params);
    }

    public function delete(int $id): bool
    {
        $sql = "DELETE FROM {$this->table} WHERE {$this->primaryKey} = ?";
        return $this->db->execute($sql, [$id]);
    }

    public function count(array $conditions = []): int
    {
        $sql = "SELECT COUNT(*) FROM {$this->table}";
        $params = [];
        
        if (!empty($conditions)) {
            $where = [];
            foreach ($conditions as $column => $value) {
                $where[] = "{$column} = ?";
                $params[] = $value;
            }
            $sql .= " WHERE " . implode(' AND ', $where);
        }
        
        $stmt = $this->db->getConnection()->prepare($sql);
        $stmt->execute($params);
        return (int) $stmt->fetchColumn();
    }

    protected function query(string $sql, array $params = []): array
    {
        return $this->db->query($sql, $params);
    }

    protected function queryOne(string $sql, array $params = []): ?array
    {
        return $this->db->queryOne($sql, $params);
    }

    protected function execute(string $sql, array $params = []): bool
    {
        return $this->db->execute($sql, $params);
    }
}
