<?php

namespace Tussendoor\Reloadify\Models;

use ReloadifyScoped\Illuminate\Database\Capsule\Manager as Capsule;
use ReloadifyScoped\Illuminate\Database\Schema\Blueprint;
use Tussendoor\Reloadify\Plugin;

class Database
{
    /**
     * The tables we use in our plugin, this array is used in the drop() method
     * The order of the tables is very important, please put all the children above their parent to prevent fatal errors
     * A table is a child when they have a foreign assigned
     *
     * @var array
     */
    private static $tables = [
        'reloadify_cart_meta',
        'reloadify_carts',
    ];

    /**
     * The instances of this class
     *
     * @var array
     */
    private static $instances = [];

    /**
     * The initialisation status of the class
     *
     * @var bool
     */
    private $initialised = false;

    /**
     * Initialise the class when using this class statically
     * By getting a Singleton instance we ensure one database connection
     *
     * @param  string $name
     * @param  array $arguments
     * @return mixed
     */
    /**
     * Initialise the class when using this class statically
     * By getting a Singleton instance we ensure one database connection
     *
     * @param  string $name
     * @param  array $arguments
     * @return mixed
     */
    public static function __callStatic(string $name, array $arguments)
    {
        $instance = self::getInstance();
        if (!method_exists($instance, $name)) return;

        $instance->setup();
        return call_user_func_array([$instance, $name], $arguments);
    }

    /**
     * Return the instance of this class
     *
     * @return self
     */
    private static function getInstance()
    {
        $cls = static::class;
        if (!isset(static::$instances[$cls])) {
            static::$instances[$cls] = new static();
        }

        return static::$instances[$cls];
    }

    /**
     * Setting the plugin tables on activation
     *
     * @return void
     * @since 1.0.4 Updated product_ids & product data to JSON (10-06-2022)
     * @since 1.0.6 Updated product_ids & product_data to string, JSON is not allways supported (16-06-2022)
     * @since 1.2.1 Made privately accessible via the __callStatic() method
     */
    private function migrate()
    {
        if (!Capsule::schema()->hasTable('reloadify_carts')) {
            Capsule::schema()->create('reloadify_carts', function (Blueprint $table) {
                $table->increments('id');
                $table->char('hash', 100)->index();
                $table->char('currency', 10);
                $table->decimal('price', 8, (wc_get_price_decimals() ?: 2));
                $table->string('recovery_url');
                $table->string('product_ids');
                $table->text('product_data');
                $table->timestamp('created_at_gmt')->nullable();
                $table->timestamps();
            });
        }

        if (!Capsule::schema()->hasTable('reloadify_cart_meta')) {
            Capsule::schema()->create('reloadify_cart_meta', function (Blueprint $table) {
                $table->increments('id');
                $table->integer('cart_id')->unsigned()->index();
                $table->char('meta_key', 100)->index();
                $table->string('meta_value');
                $table->timestamps();

                // When a abandoned cart is deleted its meta will delete as well
                $table->foreign('cart_id')->references('id')->on('reloadify_carts')->cascadeOnDelete();
            });
        }

        // Set version initially
        update_option('reloadify_plugin_db_version', Plugin::config('plugin.version'));
    }

    /**
     * Drop all tables created by the plugin
     *
     * @since 1.2.1 Made privately accessible via the __callStatic() method
     */
    private function drop()
    {
        foreach (self::$tables as $name) {
            Capsule::schema()->dropIfExists($name);
        }
    }

    /**
     * Updates the database tables if needed.
     */
    private function update()
    {
        // Ensures the 'reloadify_carts' table exists before attempting updates.
        if (Capsule::schema()->hasTable('reloadify_carts')) {
            Capsule::schema()->table('reloadify_carts', function (Blueprint $table) {
                // Check and update column types if necessary.
                // Update product_ids to string if it's not already
                if (Capsule::schema()->getColumnType('reloadify_carts', 'product_ids') !== 'string') {
                    $table->string('product_ids')->change();
                }

                // Update product_data to text (larger storage capacity)
                if (Capsule::schema()->getColumnType('reloadify_carts', 'product_data') !== 'text') {
                    $table->text('product_data')->change();
                }

                // Add 'created_at_gmt' column if it doesn't exist.
                if (!Capsule::schema()->hasColumn('reloadify_carts', 'created_at_gmt')) {
                    $table->timestamp('created_at_gmt')->nullable()->after('product_data');
                }
            });
        }
    }


    /**
     * Setup the $manager property
     *
     * @since 1.2.1 Made sure we would only create a new connection when it doesn't exist
     */
    private function setup()
    {
        if ($this->initialised) {
            return;
        }

        $settings = Plugin::config('database.settings');

        $capsule = new Capsule;
        $capsule->addConnection($settings, 'default');
        $capsule->setAsGlobal();
        $capsule->bootEloquent();

        $this->initialised = true;
    }
}
