<?php

namespace App\Controllers\Common;

use App\Libraries\Log\Log;
use App\Controllers\BaseController;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use Psr\Log\LoggerInterface;

class CommonController extends BaseController
{
    protected $_className = '';
    protected $_model = null;
    protected $_defines = array();
    protected $_viewPath = '';
    protected $_viewDatas = array();
    public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger)
    {
        parent::initController($request, $response, $logger);
        helper('Common');
        $this->_viewDatas = [
            'layout' => LAYOUTS['empty'],
            'title' => '',
            'session' => session()
        ];
        if (is_null(session()->get('uid'))) {
            redirect()->to('/login')->with('error', '로그인을하셔야합니다.');
        }
    }

    //Field별 Form Option용
    protected function getFieldFormOption(string $field): array
    {
        switch ($field) {
            default:
                $temps = lang($this->_className . '.' . strtoupper($field));
                if (!is_array($temps)) {
                    throw new \Exception(__FUNCTION__ . "에서 {$field}의 데이터가 array가 아닙니다.\n" . var_export($temps, true));
                }
                return array_merge(
                    [DEFAULT_EMPTY => lang($this->_className . '.label.' . $field) . ' 선택'],
                    lang($this->_className . '.' . strtoupper($field))
                );
                break;
        }
    }
    //Field별 Form Option용
    final protected function getFieldFormOptions(array $fieldFilters): array
    {
        $fieldFormOptions = array();
        foreach ($fieldFilters as $field) {
            if (is_array($field)) {
                throw new \Exception(__FUNCTION__ . "에서 field가 array 입니다.\n" . var_export($fieldFilters, true));
            }
            $fieldFormOptions[$field] = $this->getFieldFormOption($field);
        }
        return $fieldFormOptions;
    }
    //Field별 Form Rule용
    final protected function getFieldRules(array $fields, array $fieldRules): array
    {
        $tempRules = $this->_model->getValidationRules(['only' => $fields]);
        foreach ($fields as $field) {
            if (is_array($field)) {
                throw new \Exception(__FUNCTION__ . "에서 field가 array 입니다.\n" . var_export($fieldRules, true));
            }
            if (array_key_exists($field, $fieldRules)) {
                $tempRules[$field] = $fieldRules[$field];
            }
        }
        return $tempRules;
    }

    //Insert관련
    protected function insert_init()
    {
        $this->_viewDatas['fields'] = $this->_defines['insert']['fields'];;
        $this->_viewDatas['fieldRules'] = $this->getFieldRules($this->_viewDatas['fields'], $this->_defines['insert']['fieldRules']);
    }
    protected function insert_form_init()
    {
        $this->_viewDatas['fieldFilters'] = $this->_defines['insert']['fieldFilters'];
        $this->_viewDatas['fieldFormOptions'] = $this->getFieldFormOptions($this->_viewDatas['fieldFilters']);
        $this->_viewDatas['forms'] = ['attributes' => ['method' => "post",], 'hiddens' => []];
        helper(['form']);
    }
    protected function insert_form_process()
    {
    }
    final public function insert_form()
    {
        try {
            $this->insert_init();
            $this->insert_form_init();
            $this->insert_form_process();
            return view($this->_viewPath . '/insert', $this->_viewDatas);
        } catch (\Exception $e) {
            return alert_CommonHelper($e->getMessage(), 'back');
        }
    }

    protected function insert_validate()
    {
        //변경된 값 적용
        $fieldDatas = array();
        foreach ($this->_viewDatas['fields'] as $field) {
            $fieldDatas[$field] = $this->request->getVar($field);
        }
        $this->_viewDatas['fieldDatas'] = $fieldDatas;
        //변경할 값 확인
        if (!$this->validate($this->_viewDatas['fieldRules'])) {
            throw new \Exception("{$this->_viewDatas['title']}의 검증 오류발생\n" . implode("\n", $this->validator->getErrors()));
        }
    }
    protected function insert_process()
    {
        if (!$this->_model->save($this->_viewDatas['fieldDatas'])) {
            Log::add("error", __FUNCTION__ . "에서 호출:" . $this->_model->getLastQuery());
            Log::add("error", implode("\n", $this->_model->errors()));
            throw new \Exception(__FUNCTION__ . " 오류 발생.\n" . var_export($this->_model->errors(), true));
        }
    }
    final public function insert()
    {
        $message = "";
        try {
            $this->insert_init();
            $this->insert_validate();
            $this->insert_process();
            $message = __FUNCTION__ . " 완료하였습니다.";
            Log::save("{$this->_viewDatas['title']} {$message}");
            return alert_CommonHelper($message, session()->get(RETURN_URL));
        } catch (\Exception $e) {
            $message = __FUNCTION__ . " 실패하였습니다.";
            Log::add("warning", $e->getMessage());
            Log::add("warning", var_export($this->_viewDatas['fieldDatas'], true));
            Log::save("{$this->_viewDatas['title']} {$message}", false);
            return redirect()->back()->withInput()->with("error", $message . "<br>\n{$e->getMessage()}");
        }
    }

    //Update관련
    protected function update_init()
    {
        $this->_viewDatas['fields'] = $this->_defines['update']['fields'];;
        $this->_viewDatas['fieldRules'] = $this->getFieldRules($this->_viewDatas['fields'], $this->_defines['update']['fieldRules']);
    }
    protected function update_form_init()
    {
        $this->_viewDatas['fieldFilters'] = $this->_defines['update']['fieldFilters'];
        $this->_viewDatas['fieldFormOptions'] = $this->getFieldFormOptions($this->_viewDatas['fieldFilters']);
        $this->_viewDatas['forms'] = ['attributes' => ['method' => "post",], 'hiddens' => []];
        helper(['form']);
    }
    protected function update_form_process($entity)
    {
        return $entity;
    }
    final public function update_form($uid)
    {
        try {
            $entity = $this->_model->getEntity($uid);
            $this->update_init();
            $this->update_form_init();
            $this->_viewDatas['entity'] = $this->update_form_process($entity);
            return view($this->_viewPath . '/update', $this->_viewDatas);
        } catch (\Exception $e) {
            return alert_CommonHelper($e->getMessage(), 'back');
        }
    }
    protected function update_validate($entity)
    {
        //변경된 값 적용
        $fieldDatas = array();
        foreach ($this->_viewDatas['fields'] as $field) {
            $fieldDatas[$field] = $this->request->getVar($field);
            Log::add("info", "{$entity->getTitle()}의 {$field} : {$entity->$field}=>{$fieldDatas[$field]}");
            $entity->$field = $fieldDatas[$field];
        }
        $this->_viewDatas['fieldDatas'] = $fieldDatas;
        //변경할 값 확인
        if (!$this->validate($this->_viewDatas['fieldRules'])) {
            throw new \Exception("{$this->_viewDatas['title']}의 검증 오류발생\n" . implode("\n", $this->validator->getErrors()));
        }
        return $entity;
    }
    protected function update_process($entity)
    {
        if ($entity->hasChanged()) {
            if (!$this->_model->save($entity)) {
                Log::add("error", __FUNCTION__ . "에서 호출:" . $this->_model->getLastQuery());
                Log::add("error", implode("\n", $this->_model->errors()));
                throw new \Exception(__FUNCTION__ . " 오류 발생.\n" . var_export($this->_model->errors(), true));
            }
        } else {
            throw new \Exception(__FUNCTION__ . " 변경된값 없음.\n" . var_export($entity, true));
        }
        return $entity;
    }
    final public function update($uid)
    {
        $message = "";
        try {
            $entity = $this->_model->getEntity($uid);
            $this->update_init();
            $entity = $this->update_validate($entity);
            $entity = $this->update_process($entity);
            $message = "{$entity->getTitle()} " . __FUNCTION__ . " 완료하였습니다.";
            Log::save("{$this->_viewDatas['title']} {$message}");
            return alert_CommonHelper($message, session()->get(RETURN_URL));
        } catch (\Exception $e) {
            $message = "{$entity->getTitle()} " . __FUNCTION__ . " 실패하였습니다.";
            Log::add("warning", $e->getMessage());
            Log::add("warning", var_export($this->_viewDatas['fieldDatas'], true));
            Log::save("{$this->_viewDatas['title']} {$message}", false);
            return redirect()->back()->withInput()->with("error", $message . "<br>\n{$e->getMessage()}");
        }
    }

    //Toggle 관련
    protected function toggle_init($field)
    {
        $this->_viewDatas['fields'] = [$field];
        $this->_viewDatas['fieldRules'] = $this->getFieldRules($this->_viewDatas['fields'], array());
    }
    protected function toggle_validate($entity)
    {
        //변경된 값 적용
        $fieldDatas = array();
        foreach ($this->_viewDatas['fields'] as $field) {
            $fieldDatas[$field] = $this->request->getVar($field);
            Log::add("info", "{$entity->getTitle()}의 {$field} : {$entity->$field}=>{$fieldDatas[$field]}");
            $entity->$field = $fieldDatas[$field];
        }
        $this->_viewDatas['fieldDatas'] = $fieldDatas;
        //변경할 값 확인
        if (!$this->validate($this->_viewDatas['fieldRules'])) {
            throw new \Exception("{$this->_viewDatas['title']}의 검증 오류발생\n" . implode("\n", $this->validator->getErrors()));
        }
        return $entity;
    }
    protected function toggle_process($entity)
    {
        if ($entity->hasChanged()) {
            if (!$this->_model->save($entity)) {
                Log::add("error", __FUNCTION__ . "에서 호출:" . $this->_model->getLastQuery());
                Log::add("error", implode("\n", $this->_model->errors()));
                throw new \Exception(__FUNCTION__ . " 오류 발생.\n" . var_export($this->_model->errors(), true));
            }
        } else {
            throw new \Exception(__FUNCTION__ . " 변경된값 없음.\n" . var_export($entity, true));
        }
        return $entity;
    }
    public function toggle($uid, string $field)
    {
        $message = "";
        try {
            $entity = $this->_model->getEntity($uid);
            $this->toggle_init($field);
            $entity = $this->toggle_validate($entity);
            $entity = $this->toggle_process($entity);
            $message = "{$entity->getTitle()} " . __FUNCTION__ . " 완료하였습니다.";
            Log::save("{$this->_viewDatas['title']} {$message}");
            return alert_CommonHelper($message, session()->get(RETURN_URL));
        } catch (\Exception $e) {
            $message = "{$entity->getTitle()} " . __FUNCTION__ . " 실패하였습니다.";
            Log::add("warning", $e->getMessage());
            Log::add("warning", var_export($this->_viewDatas['fieldDatas'], true));
            Log::save("{$this->_viewDatas['title']} {$message}", false);
            return alert_CommonHelper($message . "<br>\n{$e->getMessage()}", 'back');
        }
    }
    //Batchjob 관련
    protected function batchjob_init()
    {
        //fields 해당하는 field중 선택된 값이 있는경우만 fields로 정의
        $fields = array();
        foreach ($this->_defines['index']['batchjobFilters'] as $field) {
            if ($this->request->getVar($field)) {
                array_push($fields, $field);
            }
        }
        if (!is_array($fields) || count($fields) === 0) {
            throw new \Exception($this->_viewDatas['title'] . '가 field가 정의되지 않았습니다.');
        }
        //Fields,FielRules재정의
        $this->_viewDatas['fields'] = $fields;
        $this->_viewDatas['fieldRules'] = $this->getFieldRules($this->_viewDatas['fields'], array());
    }
    protected function batchjob_validate($entity)
    {
        //변경된 값 적용
        $fieldDatas = array();
        foreach ($this->_viewDatas['fields'] as $field) {
            $fieldDatas[$field] = $this->request->getVar($field);
            Log::add("info", "{$entity->getTitle()}의 {$field} : {$entity->$field}=>{$fieldDatas[$field]}");
            $entity->$field = $fieldDatas[$field];
        }
        $this->_viewDatas['fieldDatas'] = $fieldDatas;
        //변경할 값 확인
        if (!$this->validate($this->_viewDatas['fieldRules'])) {
            throw new \Exception("{$this->_viewDatas['title']}의 검증 오류발생\n" . implode("\n", $this->validator->getErrors()));
        }
        return $entity;
    }
    protected function batchjob_process($entity)
    {
        if ($entity->hasChanged()) {
            if (!$this->_model->save($entity)) {
                Log::add("error", __FUNCTION__ . "에서 호출:" . $this->_model->getLastQuery());
                Log::add("error", implode("\n", $this->_model->errors()));
                throw new \Exception(__FUNCTION__ . " 오류 발생.\n" . var_export($this->_model->errors(), true));
            }
        } else {
            throw new \Exception(__FUNCTION__ . " 변경된값 없음.\n" . var_export($entity, true));
        }
        return $entity;
    }
    final public function batchjob()
    {
        $message = "";
        $uids = array();
        try {
            $uids = $this->request->getVar('batchjob_uids');
            if (!is_array($uids) || count($uids) === 0) {
                throw new \Exception($this->_viewDatas['title'] . '가 uid가 선택되지 않았습니다.');
            }
            $this->batchjob_init();
            foreach ($uids as $uid) {
                $entity = $this->_model->getEntity($uid);
                $entity = $this->batchjob_validate($entity);
                $entity = $this->batchjob_process($entity);
            }
            $message = "총: " . implode(",", $uids) . "의 수정(Batchjob)을 완료하였습니다.";
            Log::save("{$this->_viewDatas['title']} {$message}");
            return alert_CommonHelper($message, session()->get(RETURN_URL));
        } catch (\Exception $e) {
            $message = "총: " . implode(",", $uids) . "의 수정(Batchjob)을 실패하였습니다.";
            Log::add("warning", $e->getMessage());
            Log::add("warning", var_export($this->_viewDatas['fieldDatas'], true));
            Log::save("{$this->_viewDatas['title']} {$message}", false);
            return alert_CommonHelper($message . "<br>\n{$e->getMessage()}", 'back');
        }
    }

    //Delete 관련
    protected function delete_process($entity)
    {
        $this->_model->delete($entity->getPrimaryKey());
        return $entity;
    }
    final public function delete($uid)
    {
        $message = "";
        try {
            $entity = $this->_model->getEntity($uid);
            $entity = $this->delete_process($entity);
            $message = "{$entity->getTitle()} " . __FUNCTION__ . " 완료하였습니다.";
            Log::save("{$this->_viewDatas['title']} {$message}");
            return alert_CommonHelper($message, session()->get(RETURN_URL));
        } catch (\Exception $e) {
            $message = "{$entity->getTitle()} " . __FUNCTION__ . " 실패하였습니다.";
            Log::add("warning", $e->getMessage());
            Log::save("{$this->_viewDatas['title']} {$message}", false);
            return alert_CommonHelper($message . "<br>\n{$e->getMessage()}", 'back');
        }
    }

    //View 관련
    protected function view_init()
    {
        $this->_viewDatas['fields'] = $this->_defines['view']['fields'];
        $this->_viewDatas['fieldFilters'] = $this->_defines['view']['fieldFilters'];
        $this->_viewDatas['fieldRules'] = $this->getFieldRules($this->_viewDatas['fields'], $this->_defines['view']['fieldRules']);
        helper(['form']);
        $this->_viewDatas['fieldFormOptions'] = $this->getFieldFormOptions($this->_viewDatas['fieldFilters']);
        $this->_viewDatas['forms'] = ['attributes' => ['method' => "post",], 'hiddens' => []];
    }
    protected function view_process($entity)
    {
        return $entity;
    }
    final public function view($uid)
    {
        try {
            $entity = $this->_model->getEntity($uid);
            $this->view_init();
            $this->_viewDatas['entity'] =  $this->view_process($entity);
            return view($this->_viewPath . '/view', $this->_viewDatas);
        } catch (\Exception $e) {
            return alert_CommonHelper($e->getMessage(), 'back');
        }
    }

    //Index 관련
    protected function index_init()
    {
        $this->_viewDatas['fields'] = $this->_defines['index']['fields'];
        $this->_viewDatas['fieldFilters'] = $this->_defines['index']['fieldFilters'];
        $this->_viewDatas['batchjobFilters'] = $this->_defines['index']['batchjobFilters'];
        helper(['form']);
        $this->_viewDatas['fieldFormOptions'] = $this->getFieldFormOptions($this->_viewDatas['fieldFilters']);
        $this->_viewDatas['forms'] = ['attributes' => ['method' => "post",], 'hiddens' => []];
        session()->set(RETURN_URL, current_url() . '?' . $this->request->getUri()->getQuery());

        foreach ($this->_viewDatas['fieldFilters'] as $field) {
            $this->_viewDatas[$field] = $this->request->getVar($field) ? $this->request->getVar($field) : DEFAULT_EMPTY;
        }
        $this->_viewDatas['word'] = $this->request->getVar('word') ? $this->request->getVar('word') : '';
        $this->_viewDatas['start'] = $this->request->getVar('start') ? $this->request->getVar('start') : '';
        $this->_viewDatas['end'] = $this->request->getVar('end') ? $this->request->getVar('end') : '';
        $this->_viewDatas['order_field'] = $this->request->getVar('order_field') ? $this->request->getVar('order_field') : 'uid';
        $this->_viewDatas['order_value'] = $this->request->getVar('order_value') ? $this->request->getVar('order_value') : 'DESC';
        $this->_viewDatas['page'] = $this->request->getVar('page') ? $this->request->getVar('page') : 1;
        $this->_viewDatas['per_page'] = $this->request->getVar('per_page') ? $this->request->getVar('per_page') : DEFAULT_PERPAGE;
        $this->_viewDatas['uri'] = $this->request->getUri();
    }
    //index 모델 전처리
    private function index_setCondition()
    {
        foreach ($this->_viewDatas['fieldFilters'] as $field) {
            $value = $this->request->getVar($field) ? $this->request->getVar($field) : false;
            if ($value) {
                $this->_model->where($field, $value);
            }
        }
        $word = $this->request->getVar('word') ? $this->request->getVar('word') : '';
        if (isset($word) && $word !== '') {
            $this->_model->setIndexWordFilter($word);
        }
        $start = $this->request->getVar('start') ? $this->request->getVar('start') : '';
        $start = $this->request->getVar('end') ? $this->request->getVar('end') : '';
        if (isset($start) && $start !== '' && isset($end) && $end !== '') {
            $this->_model->setIndexDateFilter($start, $end);
        }
    }
    private function index_getRows(int $page = 0, int $per_page = 0): array
    {
        //모델 조건절 처리작업
        $this->index_setCondition();
        //OrderBy
        $order_field = $this->request->getVar('order_field') ? $this->request->getVar('order_field') : 'uid';
        $order_value = $this->request->getVar('order_value') ? $this->request->getVar('order_value') : 'DESC';
        $this->_model->setIndexOrderBy($order_field, $order_value);
        $rows = $per_page ? $this->_model->findAll($per_page, $page * $per_page - $per_page) : $this->_model->findAll();
        Log::add("debug", __METHOD__ . "에서 호출[{$per_page}:{$page}=>{$page}*{$per_page}-{$per_page}]\n" . $this->_model->getLastQuery());
        return $rows;
    }
    private function index_getPagination($pager_group = 'default', int $segment = 0, $template = 'bootstrap_full'): string
    {
        // 1.Views/Pagers/에 bootstrap_full.php,bootstrap_simple.php 생성
        // 2.app/Config/Pager.php/$templates에 'bootstrap_full => 'Pagers\bootstrap_full',
        // 'bootstrap_simple' => 'Pagers\bootstrap_simple', 추가
        $pager = \Config\Services::pager();
        // $this->_model->paginate($this->_viewDatas['per_page'], $pager_group, $this->_viewDatas['page'], $segment);
        $pager->makeLinks(
            $this->_viewDatas['page'],
            $this->_viewDatas['per_page'],
            $this->_viewDatas['total_count'],
            $template,
            $segment,
            $pager_group
        );
        $this->_viewDatas['page'] = $pager->getCurrentPage($pager_group);
        $this->_viewDatas['total_page'] = $pager->getPageCount($pager_group);
        return $pager->links($pager_group, $template);
    }
    protected function index_process()
    {
        //모델 조건절 처리작업
        $this->index_setCondition();
        //Totalcount 처리
        $total_count = $this->_model->countAllResults();
        $this->_viewDatas['total_count'] = $total_count;
        //Log::add("debug",__METHOD__."에서 호출:".$this->_model->getLastQuery());
        //줄수 처리용
        $this->_viewDatas['pageOptions'] = array("" => "줄수선택");
        for ($i = 10; $i <= $this->_viewDatas['total_count'] + $this->_viewDatas['per_page']; $i += 10) {
            $this->_viewDatas['pageOptions'][$i] = $i;
        }
        //모델 처리
        $this->_viewDatas['rows'] = $this->index_getRows((int)$this->_viewDatas['page'], (int)$this->_viewDatas['per_page']);
        //pagenation 처리
        $this->_viewDatas['pagination'] = $this->index_getPagination();
    }
    final public function index()
    {
        try {
            $this->index_init();
            $this->index_process();
            return view($this->_viewPath . '/index', $this->_viewDatas);
        } catch (\Exception $e) {
            return alert_CommonHelper($e->getMessage(), 'back');
        }
    }

    //Excel 관련
    protected function excel_init()
    {
        $this->_viewDatas['fields'] = $this->_defines['excel']['fields'];
        $this->_viewDatas['fieldFilters'] = $this->_defines['excel']['fieldFilters'];
        $this->_viewDatas['fieldFormOptions'] = $this->getFieldFormOptions($this->_viewDatas['fieldFilters']);
    }
    private function excel_getSpreadSheet()
    {
        //Excepl 초기화
        $spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
        $sheet = $spreadsheet->getActiveSheet();
        //Header용
        $column = 'A';
        foreach ($this->_viewDatas['fields'] as $field) {
            $sheet->setCellValue($column++ . '1', lang($this->_className . '.label.' . $field));
        }
        //본문용
        $line = 2;
        foreach ($this->index_getRows() as $row) {
            $column = 'A';
            foreach ($this->_viewDatas['fields'] as $field) {
                // echo "\n<BR>".var_export($this->_fieldFilters,true)."\n<BR>".var_export($fieldFormOptions,true);exit;
                $value = in_array($field, $this->_viewDatas['fieldFilters']) ? $this->_viewDatas['fieldFormOptions'][$field][$row[$field]] : $row[$field];
                $sheet->setCellValue($column . $line, $value);
                $column++;
            }
            $line++;
        }
        return $spreadsheet;
    }
    protected function excel_process()
    {
        $fileName = date('Y-m-d_Hm') . '.xlsx';
        //파일저장 참고:https://teserre.tistory.com/19
        $writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($this->excel_getSpreadSheet(), 'Xlsx');
        //파일저장용
        // $writer->save(Excel_FilePath . '/' . $fileName);
        //Download시
        header("Content-Type: application/vnd.ms-excel");
        header(sprintf("Content-Disposition: attachment; filename=%s", urlencode($fileName)));
        return $writer->save('php://output');
    }
    final public function excel()
    {
        try {
            $this->excel_init();
            return $this->excel_process();
        } catch (\Exception $e) {
            return alert_CommonHelper($e->getMessage(), 'back');
        }
    }
}
