<?php

namespace App\Models;

use CodeIgniter\Model;
use App\Libraries\Log\Log;
use App\Entities\BaseEntity;

abstract class BaseModel extends Model
{
    protected $DBGroup          = 'default';
    // protected $table            = 'user';
    protected $primaryKey       = 'uid';
    protected $useAutoIncrement = true;
    protected $insertID         = 0;
    protected $returnType       = 'array'; //object,array,entity명::class
    protected $useSoftDeletes   = false;
    protected $protectFields    = true;
    protected $allowedFields    = [];

    // Dates
    protected $useTimestamps = true;
    protected $dateFormat    = 'datetime';
    protected $createdField  = 'created_at';
    protected $updatedField  = 'updated_at';
    protected $deletedField  = 'deleted_at';

    protected $validationRules      = [];
    protected $validationMessages   = [];
    protected $skipValidation       = false;
    protected $cleanValidationRules = true;

    // Callbacks
    protected $allowCallbacks = true;
    protected $beforeInsert   = [];
    protected $afterInsert    = [];
    protected $beforeUpdate   = [];
    protected $afterUpdate    = [];
    protected $beforeFind     = [];
    protected $afterFind      = [];
    protected $beforeDelete   = [];
    protected $afterDelete    = [];

    abstract public function getEntityByField($field, $value): ?BaseEntity;
    abstract public function getEntity($uid): ?BaseEntity;
    abstract public function getFieldFormOptions(): array;

    //참고:https://www.delftstack.com/howto/php/php-uuid/#create-a-function-to-generate-v5-uuid-in-php
    // $v5_uuid = getUUIDv5_CommonTrait('8fc990b07418d5826d98de952cfb268dee4a23a3', 'delftstack!');
    final public function getUUIDv5_CommonTrait()
    {
        $n_hex = str_replace(array('-', '{', '}'), '', UUIDS['NAMESPACE']); // Getting hexadecimal components of namespace
        $binray_str = ''; // Binary value string
        //Namespace UUID to bits conversion
        for ($i = 0; $i < strlen($n_hex); $i += 2) {
            $binray_str .= chr(hexdec($n_hex[$i] . $n_hex[$i + 1]));
        }
        //hash value
        $hashing = sha1($binray_str . UUIDS['SECRET']);

        return sprintf(
            '%08s-%04s-%04x-%04x-%12s',
            // 32 bits for the time_low
            substr($hashing, 0, 8),
            // 16 bits for the time_mid
            substr($hashing, 8, 4),
            // 16 bits for the time_hi,
            (hexdec(substr($hashing, 12, 4)) & 0x0fff) | 0x5000,
            // 8 bits and 16 bits for the clk_seq_hi_res,
            // 8 bits for the clk_seq_low,
            (hexdec(substr($hashing, 16, 4)) & 0x3fff) | 0x8000,
            // 48 bits for the node
            substr($hashing, 20, 12)
        );
    }

    //계층형구조구현
    final protected function setHierarchyCreate($entity)
    {
        //자기자신이 최상위가 되게 만들기위함
        $entity->grpno = $entity->getPrimaryKey();
        // echo var_export($entity, true);
        // exit;
        //값변경후 다시 저장
        if (!$this->save($entity)) {
            Log::add("error", __FUNCTION__ . "에서 호출:" . $this->getLastQuery());
            Log::add("error", implode("\n", $this->errors()));
            throw new \Exception(__FUNCTION__ . " 오류 발생.\n" . var_export($this->errors(), true));
        }
        return $entity;
    }
    final protected function setHierarchyReply($entity, $replyEntity)
    {
        //부모의 그룹과 grpno가 같고, 부모의 grporder보다 1 큰것을 grporder+1을 해서 update
        //escape -> false옵션 반드시 있어야함
        $this->builder()->set('grporder', 'grporder+1', false);
        $this->builder()->where([
            'grpno' => $entity->grpno,
            'grporder >' => $entity->grporder
        ]);
        $this->builder()->update();
        // echo $this->getLastQuery();
        // exit;

        //reply용 설정
        $replyEntity->grpno = $entity->grpno;
        $replyEntity->grporder = $entity->grporder + 1;
        $replyEntity->grpdepth = $entity->grpdepth + 1;
        return $replyEntity;
    }

    protected function changeFormData($field, $value)
    {
        switch ($field) {
            case 'passwd':
                return $value ? password_hash($value, PASSWORD_DEFAULT) : "";
                break;
            case 'content':
                return htmlentities($value);
                break;
            case 'status':
                return $value ?: DEFAULTS['STATUS'];
                break;
            default:
                return $value;
                break;
        }
    }

    final protected function create_process($entity)
    {
        //primaryKey 할당
        if ($this->useAutoIncrement === false) {
            $pk = $this->primaryKey;
            $entity->$pk = $this->getUUIDv5_CommonTrait();
        }
        // echo var_export($entity, true);
        // exit;
        if (!$this->save($entity)) {
            Log::add("error", __FUNCTION__ . "에서 호출:" . $this->getLastQuery());
            Log::add("error", implode("\n", $this->errors()));
            throw new \Exception(__FUNCTION__ . " 오류 발생.\n" . var_export($this->errors(), true));
        }
        // echo "<HR>";
        // echo $this->getLastQuery();
        // exit;
        //primaryKey 할당
        if ($this->useAutoIncrement === true) {
            $pk = $this->primaryKey;
            $entity->$pk = $this->insertID();
        }
        return $entity;
    }
    final protected function modify_process($entity)
    {
        $entity->updated_at = time();
        if ($entity->hasChanged()) {
            if (!$this->save($entity)) {
                Log::add("error", __FUNCTION__ . "에서 호출:" . $this->getLastQuery());
                Log::add("error", implode("\n", $this->errors()));
                throw new \Exception(__FUNCTION__ . " 오류 발생.\n" . var_export($this->errors(), true));
            }
        } else {
            throw new \Exception(__FUNCTION__ . " 오류 발생.\n 기존정보와 동일하여 수정되지 않았습니다.");
        }
        return $entity;
    }

    //View관련 (게시판등의 조회수 증가함수)
    final public function increaseViewCount($uid, $field = 'view_cnt', int $cnt = 1)
    {
        //escape -> false옵션 반드시 있어야함
        $this->builder()->set($field, "{$field}+{$cnt}", false);
        $this->builder()->where($this->primaryKey, $uid);
        $this->builder()->update();
        // echo $this->getLastQuery();
        // exit;
    }

    //Index관련
    public function setIndexWordFilter(string $word)
    {
    }
    public function setIndexDateFilterTrit($start, $end)
    {
        $this->where('created_at >=', $start);
        $this->where('created_at <=', $end);
    }
    public function setIndexOrderBy($field, $order = 'ASC')
    {
        $this->orderBy($field, $order);
    }
}
