<?php

declare(strict_types=1);
/**
 * Created by Aurélien RICHAUD (16/08/2019 11:06)
 */
require_once __DIR__ . DIRECTORY_SEPARATOR . 'AnimalSac.php';

class Animal extends ObjectModel
{
    public const TABLE = 'animal';

    /**
     * @var int
     */
    public $id_customer;

    /**
     * @var string
     */
    public $espece;

    /**
     * @var string
     */
    public $nom;

    /**
     * @var string
     */
    public $race;

    /**
     * @var int
     */
    public $id_gender;

    /**
     * @var string
     */
    public $robe;

    /**
     * @var string
     */
    public $date_naissance;

    /** @var int */
    public $ration;

    /** @var int */
    public $id_product;

    /** @var int */
    public $id_product_attribute;

    /** @var string Object creation date */
    public $date_add;

    /** @var string Object last modification date */
    public $date_upd;

    /** @var bool True if address has been deleted (staying in database as deleted) */
    public $deleted = false;

    /**
     * @see ObjectModel::$definition
     *
     * @var array<string, mixed>
     */
    public static $definition = [
        'table' => self::TABLE,
        'primary' => 'id_animal',
        'multilang' => false,
        'multilang_shop' => false,
        'fields' => [
            'id_customer' => ['type' => self::TYPE_INT],
            'espece' => ['type' => self::TYPE_STRING],
            'nom' => ['type' => self::TYPE_STRING],
            'race' => ['type' => self::TYPE_STRING],
            'id_gender' => ['type' => self::TYPE_INT],
            'robe' => ['type' => self::TYPE_STRING],
            'date_naissance' => ['type' => self::TYPE_DATE],
            'ration' => ['type' => self::TYPE_INT],
            'id_product' => ['type' => self::TYPE_INT],
            'id_product_attribute' => ['type' => self::TYPE_INT],
            'date_add' => ['type' => self::TYPE_DATE, 'validate' => 'isDate', 'copy_post' => false],
            'date_upd' => ['type' => self::TYPE_DATE, 'validate' => 'isDate', 'copy_post' => false],
            'deleted' => ['type' => self::TYPE_BOOL],
        ],
    ];

    /**
     * @param int|null $id
     * @param int|null $id_lang
     * @param int|null $id_shop
     *
     * @throws PrestaShopDatabaseException
     * @throws PrestaShopException
     */
    public function __construct($id = null, $id_lang = null, $id_shop = null)
    {
        $this->ration = 0;

        parent::__construct($id, $id_lang, $id_shop);
    }

    public function hasCustomer(int $customerId): bool
    {
        return $this->id_customer == $customerId;
    }

    public function isSpecies(string $species): bool
    {
        return strtolower($this->espece) == strtolower($species);
    }

    public function getPicture(): ?string
    {
        $filename = 'a_' . md5($this->id . 'animal') . '.jpg';

        $uploadFile = _PS_UPLOAD_DIR_ . $filename;

        return is_file($uploadFile) ? 'https://' . $_SERVER['HTTP_HOST'] . '/upload/' . $filename . '?t=' . filemtime($uploadFile) : null;
    }

    /**
     * @return array<int, array<string, mixed>>|null
     *
     * @throws PrestaShopDatabaseException
     */
    public function getSacsActuels(): ?array
    {
        $db = Db::getInstance(false);

        $result = $db->executeS(
            'SELECT `as`.* FROM `' . _DB_PREFIX_ . 'animal_sac` `as` '
            . 'WHERE `as`.`id_animal` = ' . (int) $this->id . ' AND `as`.`date_achat` < NOW() AND `as`.`date_fin_estimee` >= NOW() '
            . 'ORDER BY `as`.`date_achat` ASC'
        );

        assert(is_array($result));

        return $result;
    }

    /**
     * @param bool $auto_date
     * @param bool $null_values
     *
     * @return bool Insertion result
     *
     * @throws PrestaShopDatabaseException
     * @throws PrestaShopException
     *
     * {@inheritDoc}
     */
    public function add($auto_date = true, $null_values = false)
    {
        $this->deleted = false;

        return parent::add($auto_date, $null_values);
    }

    /**
     * @param bool $null_values
     * @param bool $auto_date
     *
     * @throws PrestaShopException
     *
     * {@inheritDoc}
     */
    public function save($null_values = false, $auto_date = true): bool
    {
        if (is_string($this->espece)) {
            $this->espece = trim($this->espece);
        }
        if (is_string($this->nom)) {
            $this->nom = trim($this->nom);
        }
        if (is_string($this->race)) {
            $this->race = trim($this->race);
        }
        if (is_string($this->robe)) {
            $this->robe = trim($this->robe);
        }

        return parent::save($null_values, $auto_date);
    }

    public function delete()
    {
        $this->deleted = true;

        //@TODO Supprimer la recommandation

        return parent::save();
    }

    public function isDeleted(): bool
    {
        return (bool) (int) $this->deleted;
    }

    /**
     * @return array<int, int>|null Nombre d'années / mois ou null
     *
     * @throws Exception
     */
    public function getAge(): ?array
    {
        if (!$this->date_naissance || '0000-00-00' == $this->date_naissance) {
            return null;
        }

        $dateDiff = (new DateTime())->diff(new DateTime($this->date_naissance));

        return [$dateDiff->y, $dateDiff->m];
    }

    public function hasRecommandation(): bool
    {
        return !is_null($this->getRecommandation());
    }

    public function getRecommandation(): ?Recommandation
    {
        $query = 'SELECT MAX(id_recommandation) FROM ' . _DB_PREFIX_ . Recommandation::$definition['table'] . ' WHERE id_animal = ' . (int) $this->id . ' AND deleted = 0';
        $recommandationId = Db::getInstance()->getValue($query);

        return $recommandationId ? new Recommandation($recommandationId) : null;
    }

    public function getIdRecoCricCroc(): ?int
    {
        $db = Db::getInstance(false);

        $result = $db->getValue(
            'SELECT MAX(`r`.`id_recommandation`)
             FROM `' . _DB_PREFIX_ . 'recommandation` `r`
             WHERE `r`.`id_animal` = ' . (int) $this->id
            . ' AND `r`.`deleted` = 0'
        );

        if (!$result) {
            return null;
        }

        return (int) $result;
    }

    public function setRationReco(Recommandation $reco): void
    {
        $this->ration = $reco->qte_aliment1;

        if (!$reco->transition12 && $reco->qte_aliment2) {
            $this->ration += $reco->qte_aliment2;
        }
        if (!$reco->transition23 && $reco->qte_aliment3) {
            $this->ration += $reco->qte_aliment3;
        }
        $this->id_product = $reco->product_id_mvs1;
        $this->id_product_attribute = 0;
    }

    /**
     * @return array<string, mixed>|null
     *
     * @deprecated since 4.0.0 Utiliser getRecommandation() à la place
     */
    public function getReco(): ?array
    {
        $db = Db::getInstance(false);

        $result = $db->getRow(
            'SELECT `r`.*, `rule`.code AS discount_code, `rule`.quantity AS discount_code_quantity, `rule`.active AS discount_code_active 
             FROM `' . _DB_PREFIX_ . 'recommandation` `r`
             LEFT JOIN `' . _DB_PREFIX_ . 'cart_rule` rule ON `r`.`code_reco` = `rule`.`code`
             WHERE `r`.`id_animal` = ' . (int) $this->id
            . ' AND `r`.`deleted` = 0'
            . ' ORDER BY `r`.`id_recommandation` DESC'
        );

        if (is_array($result)) {
            return $result;
        }

        return null;
    }

    /**
     * @return array<string, mixed>|null
     */
    public function getRecoProduct(int $id_product): ?array
    {
        $db = Db::getInstance(false);

        $result = $db->getRow(
            'SELECT `p`.*, `pl`.*
             FROM `' . _DB_PREFIX_ . 'product_shop` `p`
             LEFT JOIN `' . _DB_PREFIX_ . 'product_lang` `pl` ON `pl`.`id_product` = `p`.`id_product` AND `pl`.`id_lang` = ' . (int) Context::getContext()->language->id
            . ' WHERE `p`.`id_product` = ' . (int) $id_product
            . ' AND `p`.`id_shop` = ' . (int) Context::getContext()->shop->id
        );

        if (is_array($result)) {
            return $result;
        }

        return null;
    }

    public function getRecoProductImageId(int $id_product): ?int
    {
        $db = Db::getInstance(false);

        $result = $db->getValue(
            'SELECT `i`.*
             FROM `' . _DB_PREFIX_ . 'image` `i`
             WHERE `i`.`id_product` = ' . (int) $id_product . ' AND `i`.`cover` = 1'
        );

        if (!$result) {
            return null;
        }

        return (int) $result;
    }

    public function toArray(): array
    {
        $animal = [
            'id' => $this->id,
            'photo' => $this->getPicture(),
        ];

        foreach (self::$definition['fields'] as $property => $types) {
            if (property_exists($this, $property)) {
                $animal[$property] = $this->{$property};
            }
        }

        return $animal;
    }

    /**
     * @param int $customerId
     *
     * @return Animal[]
     */
    public static function getByCustomerId(int $customerId): array
    {
        $query = 'SELECT id_animal FROM `' . _DB_PREFIX_ . self::$definition['table'] . '` WHERE id_customer = ' . $customerId . ' AND deleted = 0';

        return array_map(function ($animalId) {
            return new self($animalId);
        }, array_column(Db::getInstance()->executeS($query), 'id_animal'));
    }

    public static function hydrateAnimalWithReco(Recommandation $reco): Animal
    {
        $animal = new Animal();
        $animal->nom = $reco->petname;
        $animal->espece = strtolower($reco->espece);
        $animal->race = $reco->race;
        $animal->id_gender = $reco->id_gender;
        $animal->setRationReco($reco);
        $animal->deleted = false;

        if ($reco->date_birth && preg_match('/^[0-9]{2}-[0-9]{2}-[0-9]{4}$/', $reco->date_birth)) {
            $date = new DateTime($reco->date_birth, new DateTimeZone('Europe/Paris'));
            $animal->date_naissance = $date->format('Y-m-d');
        }

        return $animal;
    }
}
