<?php
// +-------------------------------------------------+
// © 2002-2004 PMB Services / www.sigb.net pmb@sigb.net et contributeurs (voir www.sigb.net)
// +-------------------------------------------------+
// $Id: OrmManyToMany.php,v 1.13.2.2.6.5 2026/03/26 13:19:59 qvarin Exp $

namespace Pmb\Common\Orm;

use Pmb\Common\Exceptions\NotFoundException;
use Pmb\Common\Exceptions\StructuresOrmException;
use Pmb\Common\Exceptions\UnableToDeleteException;
use Pmb\Common\Helper\Helper;

abstract class OrmManyToMany extends Orm
{

    protected $ids = array();

    /**
     * Respecter le format de tableau : [ nom_du_champ => id ]
     *
     * @param array $ids
     */
    public function __construct(array $ids = array())
    {
        $this->ids = $ids;
        $this->initDataStructure();
        if (! empty($ids)) {
            $this->fetchData();
        }
    }

    /**
     * Fetch data from database
     *
     * @param int $id
     * @return static
     */
    protected function fetchData(int $id = 0)
    {
        $query = 'SELECT * FROM ' . static::$tableName . ' WHERE ';
        $i = 0;
        foreach ($this->ids as $field => $value) {
            if ($i) {
                $query .= " AND ";
            }
            $query .= "$field = $value";
            $i ++;
        }
        $result = pmb_mysql_query($query);
        if (pmb_mysql_num_rows($result)) {
            $row = pmb_mysql_fetch_assoc($result);
            return $this->feedObject($row);
        }
    }

    /**
     * Check if record exists
     *
     * @param int $id
     * @return boolean
     */
    public static function exist($ids = [])
    {
        if (! is_array($ids)) {
            return false;
        }
        $query = "SELECT 1 FROM " . static::$tableName . " WHERE ";
        $i = 0;

        if(empty($ids)) {
            return false;
        }

        foreach ($ids as $field => $value) {
            if ($i) {
                $query .= " AND ";
            }
            $query .= "$field = $value";
            $i ++;
        }
        $result = pmb_mysql_query($query);
        return pmb_mysql_num_rows($result) == 1;
    }

    public function saveRelations()
    {
        foreach (static::$relations[static::class] as $name => $relation) {
            switch ($relation['Relation']) {
                case static::MANY_TO_MANY:
                    foreach ($this->$name as $relatedObject) {
                        $query = "REPLACE INTO {$relation['TableLink']} ({$relation['ForeignKey']}, {$relation['RelatedKey']}) VALUES ('{$this->{static::$idTableName}}', '{$relatedObject->{Helper::camelize($relation['Orm']::$idTableName)}}')";
                        @pmb_mysql_query($query);
                    }
                    break;
                default:
                    break;
            }
        }
    }

    public function save()
    {
        $query = "REPLACE INTO " . static::$tableName . " (" . implode(',', array_keys(static::$structures[static::class])) . ") VALUES ('";
        $values = [];
        foreach (static::$structures[static::class] as $key => $val) {
            // TODO Addslashes plus fin...
            $values[] = addslashes($this->{$key});
        }
        $query .= implode("','", $values) . "')";

        pmb_mysql_query($query);
    }

    /**
     * Delete the current instance
     *
     * @throws UnableToDeleteException
     * @return void
     */
    public function delete()
    {
        if (! is_array($this->ids) && ! count($this->ids)) {
            throw new UnableToDeleteException("Instance with IDs " . implode(', ', $this->ids) . " cannot be deleted");
        }

        if (! $this->checkBeforeDelete()) {
            throw new UnableToDeleteException("Instance cannot be deleted");
        }

        $query = "DELETE FROM " . static::$tableName . " WHERE ";

        $i = 0;
        foreach ($this->ids as $field => $value) {
            if ($i) {
                $query .= " AND ";
            }
            $query .= "$field = $value";
            $i ++;
        }
        pmb_mysql_query($query);
        $defaultProperties = static::$reflectionClass[static::class]->getDefaultProperties();
        foreach (static::$structures[static::class] as $key => $val) {
            $this->{$key} = $defaultProperties[$key];
        }
    }

    /**
     * Effectuer une recherche sur une colonne
     *
     * @return array
     */
    public static function find($field, $value, $orderby = "")
    {
        $query = "SELECT * FROM " . static::$tableName . " WHERE $field = '" . addslashes($value) . "'" . ($orderby ? " ORDER BY $orderby" : '');
        $result = pmb_mysql_query($query);
        $instances = array();
        if (pmb_mysql_num_rows($result)) {
            $className = static::class;
            foreach ($result as $row) {
                $ids = $row;

                if(isset($className::$idsTableName) && !empty($className::$idsTableName)) {
                    foreach($ids as $prop => $value) {
                        if(!in_array($prop, $className::$idsTableName)) {
                            unset($ids[$prop]);
                        }
                    }
                }

                $instances[] = new $className($ids);
            }
        }
        return $instances;
    }

    /**
     * Effectuer une recherche sur plusieurs colonnes (Inter : AND)
     *
     * @param array $fieldsValues
     * @param string $orderby
     * @param string $inter
     * @param string $limit
     *
     * @return array
     */
    public static function finds(array $fieldsValues, $orderby = "", $inter = "AND", $limit = "")
    {
        $query = "SELECT * FROM " . static::$tableName . " WHERE ";
        $clause = "";
        foreach ($fieldsValues as $field => $data) {
            $operator = self::DEFAULT_OPERATOR;
            if (is_string($data) || is_numeric($data)) {
                $value = $data;
            } elseif (is_array($data) && isset($data['value']) && isset($data['operator'])) {
                $value = $data['value'] ?? null;
                $operator = $data['operator'] ?? self::DEFAULT_OPERATOR;
            } else {
                continue;
            }

            if (! empty($clause)) {
                $clause .= " {$inter} ";
            }
            $clause .= "{$field} {$operator} '" . addslashes($value) . "'";
        }
        $query .= $clause;
        $query .= ($orderby ? " ORDER BY {$orderby}" : '');
        $query .= ($limit ? " LIMIT {$limit}" : '');

        $result = pmb_mysql_query($query);
        $instances = array();
        if (pmb_mysql_num_rows($result)) {
            $className = static::class;
            foreach ($result as $row) {
                $ids = $row;

                if(isset($className::$idsTableName) && !empty($className::$idsTableName)) {
                    foreach($ids as $prop => $value) {
                        if(!in_array($prop, $className::$idsTableName)) {
                            unset($ids[$prop]);
                        }
                    }
                }

                $instances[] = new $className($ids);
            }
        }
        return $instances;
    }

    /**
     * Initialize the data structure for the current class
     *
     * @throws StructuresOrmException
     * @return array
     */
    protected function initDataStructure()
    {
        if (!isset(static::$reflectionClass[static::class])) {
            static::$reflectionClass[static::class] = new \ReflectionClass($this);
        }
        $query = "show columns from " . static::$tableName;
        $result = pmb_mysql_query($query);
        if (pmb_mysql_num_rows($result)) {
            while ($row = pmb_mysql_fetch_object($result)) {
                static::$structures[static::class][$row->Field] = $row;

                // On verifie que la propriete existe sur l'ORM
                $rowField = $row->Field;
                if (! empty(static::$tablePrefix)) {
                    $rowField = str_replace(static::$tablePrefix . '_custom_', '', $row->Field);
                }

                if (false === static::$reflectionClass[static::class]->hasProperty($rowField)) {
                    throw new StructuresOrmException("$rowField is missing");
                }
            }
        }
        return static::$structures[static::class];
    }

    public static function getInstance($ids = array())
    {
        $ids = is_array($ids) ? $ids : [$ids];
        static::$instances[static::class] = static::$instances[static::class] ?? [];
        $id = implode(',', $ids);

        if (isset(static::$instances[static::class][$id])) {
            $instance = static::$instances[static::class][$id];
        } else {
            /** @phpstan-ignore new.static */
            $instance =  new static($ids);
            static::$instances[static::class][$id] = $instance;
        }

        return $instance;
    }
}