<?php

namespace RtmBusiness\PostSync\Model;

use DateTime;
use Exception;
use mysqli_result;
use RtmBusiness\PostSync\Query;

abstract class BaseModel
{

    /**
     * @throws Exception
     */
    public function __construct(array $properties = [])
    {
        if (!empty($properties)) {
            foreach ($properties as $property => $value) {
                if (strtolower($property) === 'timestamp' || strtolower($property) === 'datetime') {
                    $this->{$property} = new DateTime($value);
                } else {
                    $this->{$property} = maybe_unserialize($value);
                }
            }
        }
    }

    /**
     * Creates new entity of model
     * @param array $properties model properties
     * @return static entity object
     * @throws Exception
     */
    public static function create(array $properties): self
    {
        return new static($properties);
    }

    /**
     * Saves entity to the database, either insert it or updates it
     * @return int - inserted primary key
     */
    public function save(): int
    {
        global $wpdb;
        $properties = $this->formatProperties($this->getProperties());

        // If there is no primary key, insert a new record. Otherwise, update current record
        if (!isset($properties[static::getPrimaryKey()]) || is_null($properties[static::getPrimaryKey()])) {
            $wpdb->insert(static::getTable(), $properties);
            $this->{static::getPrimaryKey()} = $wpdb->insert_id;
        } else {
            $wpdb->update(static::getTable(), $properties, [static::getPrimaryKey() => $this->{static::getPrimaryKey()}]);
        }

        return $this->{static::getPrimaryKey()};
    }

    /**
     * Deletes entity from database
     * @return mysqli_result|bool|int|null
     */
    public function delete(): mysqli_result|bool|int|null
    {
        global $wpdb;
        return $wpdb->delete(static::getTable(), [static::getPrimaryKey() => $this->{static::getPrimaryKey()}]);
    }

    /**
     * Gets the query object for the entity
     * @return Query
     */
    public static function query(): Query
    {
        return new Query(get_called_class());
    }

    /**
     * Get all model properties
     * @return array
     */
    private function getProperties(): array
    {
        return get_object_vars($this);
    }

    /**
     * Format all properties for query execution
     * @param $properties
     * @return array
     */
    private function formatProperties($properties): array
    {
        unset($properties['db']);
        unset($properties['table']);
        unset($properties['primaryKey']);

        foreach ($properties as $property => $value) {
            if (is_object($value) && get_class($value) === 'DateTime') {
                $properties[$property] = $value->format('Y-m-d H:i:s');
            } else if (is_array($value)) {
                $properties[$property] = maybe_serialize($value);
            } else {
                $properties[$property] = esc_sql($value);
            }
        }
        return $properties;
    }

    /**
     * Get the column names to search on
     * @return array list of all search columns
     */
    abstract public static function getSearchFields(): array;

    /**
     * Gets the name of the table
     * @return string name of the table
     */
    abstract public static function getTable(): string;

    /**
     * Gets the primary column name
     * @return string name of the column
     */
    abstract public static function getPrimaryKey(): string;
}
