<?php

namespace App\Services;

use App\Helpers\CommonHelper;
use App\Models\CommonModel;
use App\Services\Customer\ClientService;
use App\Services\UserService;
use App\Services\MyLogService;

abstract class CommonService
{
    private $_model = null;
    private $_helper = null;
    private $_classNames = [];
    private $_control = [];
    private ?UserService $_userService = null;
    private ?ClientService $_clientService = null;
    private ?MyLogService $_mylogService = null;
    protected function __construct(CommonModel $model, CommonHelper $helper)
    {
        $this->_model = $model;
        $this->_helper = $helper;
    }
    abstract public function getFormFields(): array;
    abstract public function getFormFilters(): array;
    final protected function getModel(): mixed
    {
        if (!$this->_model instanceof CommonModel) {
            throw new \Exception(__METHOD__ . "에서 오류발생:Model이 정의되지 않았습니다. ");
        }
        return $this->_model;
    }
    final public function getHelper(): mixed
    {
        if (!$this->_helper instanceof CommonHelper) {
            throw new \Exception(__METHOD__ . "에서 오류발생:Helper가 정의되지 않았습니다. ");
        }
        return $this->_helper;
    }
    final public function getControlDatas(?string $key = null): string|array
    {
        if (!$key) {
            return $this->_control;
        }
        return array_key_exists($key, $this->_control) ? $this->_control[$key] : [];
    }
    final public function setControlDatas(string $key, mixed $values): void
    {
        if (!array_key_exists($key, $this->_control)) {
            $this->_control[$key] = [];
        }
        $this->_control[$key] = $values;
    }
    final protected function addClassName(string $className): void
    {
        $this->_classNames[] = $className;
    }
    final public function getClassName($delimeter = DIRECTORY_SEPARATOR): string
    {
        return implode($delimeter, $this->_classNames);
    }
    final public function setAction(string $action): void
    {
        $this->setControlDatas('action', $action);
    }
    final public function getAction(): string
    {
        if (!$this->getControlDatas('action')) {
            throw new \Exception("action이 정의되지 않았습니다.");
        }
        return $this->getControlDatas('action');
    }
    //FormFields정의
    final public function setFormFields(?array $fields = null): void
    {
        switch ($this->getAction()) {
            case 'index':
                $actionFields = $this->getIndexFields();
                break;
            case 'view':
                $actionFields = $this->getViewFields();
                break;
            default:
                $actionFields = $this->getFormFields();
                break;
        }
        $this->setControlDatas('actionFields', is_array($fields) ? $fields : $actionFields);
    }
    final public function setFormFilters(?array $filters = null): void
    {
        switch ($this->getAction()) {
            case 'index':
                $actionFilters = $this->getIndexFilters();
                break;
            case 'view':
                $actionFilters = $this->getViewFilters();
                break;
            default:
                $actionFilters = $this->getFormFilters();
                break;
        }
        $this->setControlDatas('actionFilters', is_array($filters) ? $filters : $actionFilters);
    }
    //FormRules정의
    final public function getFormRules(array $rules = []): array
    {
        foreach ($this->getControlDatas('field_rules') as $field => $rule) {
            $rules[$field] = $rule;
        }
        return $rules;
    }
    final public function setFormRules(): void
    {
        $rules = [];
        foreach ($this->getControlDatas('actionFields') as $field) {
            $rules[$field] = $this->getFormRule($this->getAction(), $field);
        }
        $this->setControlDatas('field_rules', $rules);
    }
    //FormOptions정의
    final protected function getFormOptions(array $options = []): array
    {
        foreach ($this->getControlDatas('field_optons') as $field => $option) {
            $options[$field] = $option;
        }
        return $options;
    }
    final public function setFormOptions(): void
    {
        //기존 Filter Options 가져와서 field에 해당하는 option이 없으면 field를 key로 배열추가 후 다시 filter_options 전체 적용
        $options = [];
        foreach ($this->getControlDatas('actionFilters') as $field) {
            $options[$field] = $this->getFormOption($field, $options);
        }
        $this->setControlDatas('field_optons', $options);
    }
    //Entity별로 작업처리시
    final public function getLastQuery(): string
    {
        return $this->getModel()->getLastQuery();
    }
    final protected function getEntity_process(mixed $entity): mixed
    {
        return $entity;
    }
    final public function getEntity(string|int|array $where, ?string $message = null): mixed
    {
        try {
            $entity = is_array($where) ? $this->getModel()->where($where)->first() : $this->getModel()->find($where);
            if (!$entity) {
                return null;
            }
            if (is_array($entity)) {
                throw new \Exception(__METHOD__ . "에서 결과값 Array 오류발생:\n" . var_export($entity, true));
            }
            return $this->getEntity_process($entity);
        } catch (\Exception $e) {
            $message = sprintf(
                "\n------%s SQL오류-----<BR>\n%s\n%s\n------------------------------\n",
                __FUNCTION__,
                $this->getLastQuery(),
                $e->getMessage()
            );
            throw new \Exception($message);
        }
    }
    //entities를 가져오는 조건
    protected function getEntities_process(mixed $where = null, array $columns = ['*']): array
    {
        if ($where) {
            $this->getModel()->where($where);
        }
        //출력순서 정의
        $this->setOrderBy();
        $entities = [];
        foreach ($this->getModel()->select(implode(',', $columns))->findAll() as $entity) {
            $entities[$entity->getPK()] = $this->getEntity_process($entity);
        }
        return $entities;
    }
    final public function getEntities(mixed $where = null, array $columns = ['*']): array
    {
        try {
            return $this->getEntities_process($where, $columns);
        } catch (\Exception $e) {
            $message = sprintf(
                "\n------%s SQL오류-----<BR>\n%s\n%s\n------------------------------\n",
                __FUNCTION__,
                $this->getModel()->getLastQuery(),
                $e->getMessage()
            );
            throw new \Exception($message);
        }
    } //
    final public function getUserService(): UserService
    {
        if (!$this->_userService) {
            $this->_userService = new UserService();
        }
        return $this->_userService;
    }
    final public function getClientService(): ClientService
    {
        if (!$this->_clientService) {
            $this->_clientService = new ClientService();
        }
        return $this->_clientService;
    }
    final public function getMylogService(): MyLogService
    {
        if (!$this->_mylogService) {
            $this->_mylogService = new MyLogService();
        }
        return $this->_mylogService;
    }
    final public function getLatestPK(): int
    {
        $row = $this->getModel()->selectMax($this->getModel()->getPKField())->get()->getRow();
        return isset($row->uid) ? ((int)$row->uid + 1) : 1;
    }
    //기본 기능부분
    //FieldForm관련용
    final public function getFormDatas(): array
    {
        return $this->getControlDatas('form_datas');
    }
    final public function setFormDatas(array $requestDatas, array $formDatas = []): void
    {
        foreach ($this->getControlDatas('actionFields') as $field) {
            $formDatas = $this->setFormData($field, $requestDatas, $formDatas);
        }
        $this->setControlDatas('form_datas', $formDatas);
    }
    public function getViewFields(): array
    {
        return $this->getFormFields();
    }
    public function getViewFilters(): array
    {
        return $this->getFormFilters();
    }
    public function getIndexFields(): array
    {
        return $this->getFormFields();
    }
    public function getIndexFilters(): array
    {
        return $this->getFormFilters();
    }
    public function getBatchjobFields(): array
    {
        return $this->getIndexFilters();
    }
    public function getBatchjobButtons(): array
    {
        return [
            'batchjob' => '일괄 처리',
            'batchjob_delete' => '일괄 삭제',
        ];
    }
    public function getFormOption(string $field, array $options = []): array
    {
        switch ($field) {
            case 'user_uid':
                $options = $this->getUserService()->getEntities();
                break;
            case 'clientinfo_uid':
                $options = $this->getClientService()->getEntities();
                break;
            default:
                $options = lang($this->getClassName() . '.' . strtoupper($field));
                break;
        }
        if (!is_array($options)) {
            throw new \Exception(__FUNCTION__ . "에서 {$field}의 options 값들이 array가 아닙니다.\n" . var_export($options, true));
        }
        return $options;
    }
    public function getFormRule(string $action, string $field): string
    {
        if (is_array($field)) {
            throw new \Exception(__FUNCTION__ . "=> field가 array 입니다.\n" . var_export($field, true));
        }
        switch ($field) {
            default:
                $rule = $this->getModel()->getFormRule($action, $field);
                break;
        }
        return $rule;
    }
    public function setFormData(string $field, array $requestDatas, array $formDatas): array
    {
        switch ($field) {
            default:
                $formDatas[$field] = $requestDatas[$field] ?? null;
                break;
        }
        return $formDatas;
    }
    //생성
    public function create(array $formDatas): mixed
    {
        $entity = $this->getEntity_process($this->getModel()->create($formDatas));
        // LogCollector::info("[{$entity->getTitle()}]" . MESSAGES["CREATED"] . ":");
        return $entity;
    }
    //수정
    public function modify(mixed $entity, array $formDatas): mixed
    {
        $entity = $this->getEntity_process($this->getModel()->modify($entity, $formDatas));
        // LogCollector::info("[{$entity->getTitle()}]" . MESSAGES["UPDATED"] . ":");
        return $entity;
    }
    //단일작업
    public function toggle(mixed $entity, array $formDatas): mixed
    {
        $entity = $this->getEntity_process($this->getModel()->modify($entity, $formDatas));
        // LogCollector::info("[{$entity->getTitle()}]" . MESSAGES["UPDATED"] . ":");
        return $entity;
    }
    //일괄처리작업
    public function batchjob(mixed $entity, array $formDatas): mixed
    {
        $entity = $this->getEntity_process($this->getModel()->modify($entity, $formDatas));
        // LogCollector::info("[{$entity->getTitle()}]" . MESSAGES["UPDATED"] . ":");
        return $entity;
    }
    //삭제
    public function delete(mixed $entity): mixed
    {
        try {
            $message = "[{$entity->getTitle()}]" . MESSAGES["DELETED"] . ":";
            if (!$this->getModel()->delete($entity->getPK())) {
                throw new \Exception(var_export($this->getModel()->errors(), true));
            }
            // LogCollector::info($message);
            return $entity;
        } catch (\Exception $e) {
            $message = sprintf(
                "\n------%s SQL오류-----\n%s\n%s\n------------------------------\n",
                __FUNCTION__,
                $this->getModel()->getLastQuery(),
                $e->getMessage()
            );
            throw new \Exception($message);
        }
    }
    ////Index 검색용
    //Index용
    final public function getTotalCount(): int
    {
        return $this->getModel()->countAllResults(false);
    }
    //Limit처리
    final public function setLimit(int $per_page): void
    {
        $this->getModel()->limit($per_page);
    }
    //Offset처리
    final public function setOffset(int $offset): void
    {
        $this->getModel()->offset($offset);
    }
    //FormFilter 조건절 처리
    public function index_condition_filterField(string $field, mixed $filter_value): void
    {
        switch ($field) {
            default:
                $this->getModel()->where("{$this->getModel()->getTable()}.{$field}", $filter_value);
                break;
        }
    }
    //검색어조건절처리
    public function index_condition_filterWord(string $word): void
    {
        $this->getModel()->orLike($this->getModel()->getTable() . "." . $this->getModel()->getTitleField(), $word, 'both');
    }
    //날자검색
    public function index_condition_filterDate(string $start, string $end): void
    {
        $this->getModel()->where(sprintf("%s.created_at >= '%s 00:00:00'", $this->getModel()->getTable(), $start));
        $this->getModel()->where(sprintf("%s.created_at <= '%s 23:59:59'", $this->getModel()->getTable(), $end));
    }
    //OrderBy 처리
    public function setOrderBy(mixed $field = null, mixed $value = null): void
    {
        if ($field !== null && $value !== null) {
            $this->getModel()->orderBy(sprintf("%s.%s %s", $this->getModel()->getTable(), $field, $value));
        }
    }
}
