<?php

declare(strict_types=1);

namespace App\Http\Controllers\Api\Nasa\V1\Base;

use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use InvalidArgumentException;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;

abstract class ListController
{
    private string $sOperatorDefault = 'eq';
    private string $sSortDefault = 'asc';
    
    private array $aFilterParams = [
        'c' => [],
        'cc' => [],
        'ob' => [],
    ];
    
    protected $oModel;
    protected array $aValueFields = [];
    protected string $sPageName = '';
//    protected string $sModelName = '';
    protected string $sModelPath = '';
    protected array $aFilterFields = []; // 允许where的字段
//    protected array $aArticleFields = []; // 文章字段
    protected array $aFieldTypes = [];
    protected array $aLenthFields = []; // 限制长度的字段
    protected array $aSelectFields = []; // 默认作为select的字段
    protected array $aListFields = []; // list展示的字段和名字映射
    protected array $aDateFields = []; // date字段
    protected array $aJsonFields = []; // json字段
//    protected array $aValueStyle = []; // 特定文字渲染样式
    protected int $iLimitMax = 100; // 默认limit限制
    protected int $iLimit = 20; // 默认显示行数
    protected array $aCcExtFields = []; // 自定义条件字段追加在listFields后
    protected array $aCcFields = [];
    protected int $iOrderByMax = 3;
    protected array $aOrderBy = [
        [
            "sField"    => "id",
            "sSort"     => "desc"
        ]
    ];
    protected array $aValueStyle = [ // 特定文字渲染样式
        '关闭'    => "<span class='text-danger'>关闭</span>",
        '开启'    => "<span class='text-success'>开启</span>"
    ];
    
    public function __construct()
    {
        if ($this->sModelPath) {
            if (! class_exists($this->sModelPath)) {
                throw new InvalidArgumentException("【系统架构错误】: 找不到领域模型 {$this->sModelPath}");
            }

            $this->oModel = new $this->sModelPath();
            $this->aCcFields = array_merge($this->aListFields, $this->aCcExtFields);
            $this->aCcFields = $this->getCcFields();
        }
    }
    
    private function getCcFields()
    {
        $a = [];
        foreach ($this->aCcFields as $k => $v) {
            if (is_array($v)) {
                foreach ($v as $kk => $vv) {
                    $a[$kk] = $vv;
                }
            } else {
                $a[$k] = $v;
            }
        }
        
        return $a;
    }
    
    private function mapOperator($sOperator)
    {
        $sSqlOperator = match ($sOperator) {
            'like' => 'like',
            'eq'   => '=',
            'ne'   => '!=',
            'gt'   => '>',
            'gte'  => '>=',
            'lt'   => '<',
            'lte'  => '<=',
            default => '',
        };
        
        return $sSqlOperator;
    }
    
    private function getOrderBySchema()
    {
        $iOrderByCount = count($this->aOrderBy);
        $iDiff = $this->iOrderByMax - $iOrderByCount;
        $a = $this->aOrderBy;
        for ($i=0; $i<$iDiff; $i++) {
            $a[] = [];
        }
        return $a;
    }
    
    private function getOrderBy()
    {
        return $this->aOrderBy;
    }
    
    public function getListFields()
    {
        $a = [];
        
        foreach ($this->aListFields as $k => $v) {
            if ($v) {
                $a[$k] = $v;
            } else {
                $a[$k] = $k;
            }
        }
        
        return $a;
    }
    
    private function getCcLast()
    {
        $aCcFieldsTmp = $this->aCcFields;
        
        foreach ($this->aFilterParams['cc'] as $ACC) {
            unset($aCcFieldsTmp[$ACC['sField']]);
        }
        
        $s = head(array_keys($aCcFieldsTmp));
        
        return $s;
    }
    
    public function add(Request $oRequest): \Illuminate\Http\JsonResponse
    {
        $sTableName = $this->oModel->getTable();
        $aRawFields = Schema::getColumnListing($sTableName);
        
        $sClassName = get_class($this->oModel);

        $aKickFields = [];
        
        if (defined($sClassName . '::UPDATED_AT')) {
            $aKickFields[] = constant($sClassName . '::UPDATED_AT');
        }
        
        if (defined($sClassName . '::CREATED_AT')) {
            $aKickFields[] = constant($sClassName . '::CREATED_AT');
        }
        
        $aKickFields[] = "id";
        
        $aRawFieldsClear = [];
        foreach ($aRawFields as $aRawField) {
            if (in_array($aRawField, $aKickFields)) {
                continue;
            }
            $aRawFieldsClear[] = $aRawField;
        }
        
        return response()->json([
            'iCode' => 200,
            'aData'   => [
                'cList' => $aRawFieldsClear,
                'sPageName' => $this->sPageName
            ]
        ], 200);
    }
    
    public function clone(Request $oRequest)
    {
        $iId = request('id');
        $oModelResult = $this->oModel->findOrFail($iId);
        $cList = $oModelResult->getRawOriginal();
        
        $sTableName = $this->oModel->getTable();
        $aStatus = DB::select("SHOW TABLE STATUS LIKE '{$sTableName}'");
        $iNextId = $aStatus[0]->Auto_increment ?? 1;
        
        $cList['id'] = $iNextId;
        
        return response()->json([
            'iCode' => 200,
            'aData'   => [
                'cList' => $cList,
                'aMap' => $this->getMapAll(),
                'aListField' => $this->getListFields(),
                'sPageName' => $this->sPageName
            ]
        ], 200);
    }
    
//    public function cloneSave(Request $oRequest)
//    {
//        $iId = request('id');
//
//        $aParams = $oRequest->all();
//        
//        foreach ($aParams as $k => $v) {
//            $this->oModal->{$k} = $v;
//        }
//        $this->oModel->create();
//
//        return response()->json([
//            'iCode' => 200,
//            'aData' => [],
//            'sMsg'  => "保存成功：id ".$this->oModel->id
//        ], 200);
//    }
    
    public function detail(Request $oRequest)
    {
        $iId = request('id');
        $oModelResult = $this->oModel->findOrFail($iId);
        $cList = $oModelResult->getRawOriginal();
        
        return response()->json([
            'iCode' => 200,
            'aData'   => [
                'cList' => $cList,
                'aMap' => $this->getMapAll(),
                'aListField' => $this->getListFields(),
                'sPageName' => $this->sPageName
            ]
        ], 200);
    }
    
    public function hit(Request $oRequest)
    {
        $iId = request('id');
        $sField = request('field');
        
        $sFieldType = $this->aFieldTypes[$sField] ?? '';
        
        $sHit = $this->oModel->find($iId)?->{$sField};
        if (is_array($sHit)) {
            $sHit = json_encode($sHit);
        }
        
        if ($sFieldType == 'json') {
            $sHit = json_decode($sHit, true);
            $sHit = json_encode($sHit, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        }
        
        return response()->json([
            'iCode' => 200,
            'aData'   => [
                'sHit' => $sHit
            ]
        ], 200);
        
    }
    
    public function delete(Request $oRequest)
    {
        $iId = request('id');
        
        $this->oModel->findOrFail($iId)->delete();
        
        return response()->json([
            'iCode' => 200,
            'aData' => [],
            'sMsg'  => '删除成功'
        ], 200);
    }
    
    public function addSave(Request $oRequest)
    {
        $aParams = $oRequest->all();

        foreach ($aParams as $k => $v) {
            $this->oModel->{$k} = $v;
        }
        
        if (!$this->oModel->save()) {
            return response()->json(['code' => 500, 'msg' => '保存失败？'], 500);
        }
        
        return response()->json([
            'iCode' => 200,
            'aData'   => [],
            'sMsg'  => '保存成功'
        ], 200);
    }
    
    public function detailSave(Request $oRequest)
    {
        $iId = request('id');
                
        $oThisModel = $this->oModel->find($iId);

        $aParams = $oRequest->all();

        unset($aParams['id'], $aParams['_method']);

        foreach ($aParams as $k => &$v) {
            if (empty($v) && empty($oThisModel->{$k})) {
                continue;
            }
            if (empty($v) && $v !== 0 && $v !== '0') {
                $v = '';
            }
            if ($oThisModel->{$k} != $v) {
                $oThisModel->{$k} = $v;
            }
        }

        $oThisModel->save();

        $aChangeField = $oThisModel->getChanges();
        
        /// 排除update_at
        $sClassName = get_class($this->oModel);
        if (
            defined($sClassName . '::UPDATED_AT')
            && ($sUpdateColumn = constant($sClassName . '::UPDATED_AT'))
            && !isset($aParams[$sUpdateColumn])
            && isset($aChangeField[$sUpdateColumn])
        ) {
            unset($aChangeField[$sUpdateColumn]);
        }
        
        /// 构造msg
        $sMsg = "";
        $sJiantou = " -> ";
        foreach ($aChangeField as $k => $v) {
            
            if (mb_strlen($v, 'UTF-8') > 60 ) {
                $sJiantou = '';
//                $v = mb_substr($v, 0, 60, 'UTF-8');
                $v = '';
            }
            
            if (!isset($aParams[$k])) {
                $sMsg .= "⚠️ ".$k . $sJiantou . $v . "<br><br>";
            } else {
                $sMsg .= "✅ ".$k . $sJiantou . $v . "<br><br>";
                unset($aParams[$k]);
            }
        }
        foreach ($aParams as $k => $v) {
            
            if (mb_strlen($v, 'UTF-8') > 60 ) {
                $sJiantou = '';
//                $v = mb_substr($v, 0, 60, 'UTF-8');
                $v = '';
            }
            
            $sMsg .= "❌ ".$k . $sJiantou . $v . "<br><br>";
        }
        $sMsg = rtrim($sMsg, "<br><br>");
        
        return response()->json([
            'iCode' => 200,
            'aData' => $aChangeField,
            'sMsg'  => $sMsg
        ], 200);
    }
    
    public function getMapAll()
    {
        $a = [];
        
        if (method_exists($this->oModel, 'getMapAll')) {
            $a = $this->oModel->getMapAll();
        }
        
        $b = array_merge($a, $this->aValueFields);
        
        return $b;
    }
    
    public function index(Request $oRequest)
    {
        if (! $this->oModel) {
            return response()->json(['code' => 500, 'msg' => '未绑定核心领域模型'], 500);
        }

        $oQuery = $this->oModel->newQuery();
        
        $aParams = $oRequest->all();

        foreach ($aParams as $k => $v) {
            
            if (!empty($this->aFilterFields)) {
                if (!in_array($k, $this->aFilterFields)) {
                     continue;
                }
            }
            
            // w__name__eq=1
            $aK = explode("__", $k);
            $sType = $aK[0] ?? '';
            $sField = $aK[1] ?? '';
            $sOperator = $aK[2] ?? $this->sOperatorDefault;
            $sSqlOperator = $this->mapOperator($sOperator);

            if (!isset($this->aFilterParams[$sType])) {
                continue;
            }

            if (!$sType || !$sField) {
                continue;
            }
            
            $this->aFilterParams[$k] = $v;
            
            if ($sType === 'ob') {
                $this->aFilterParams[$sType][] = [
                    'sField'    => $sField,
                    'sSort'     => $v ?? $this->sSortDefault
                ];
                continue;
            }
            
            if ($sType === 'c') { // 预制条件，字典式
                $this->aFilterParams[$sType][$sField] = [
                    'sOperator' => $sOperator,
                    'sValue'    => $v
                ];
            }
            
            if ($sType === 'cc') { // 自定义条件，集合式
                $this->aFilterParams[$sType][] = [
                    'sField'    => $sField,
                    'sOperator' => $sOperator,
                    'sValue'    => $v
                ];
            }
            
            if (blank($v)) continue; // 空v可以back但不加入sql查询
            
            $sSqlValue = $v;
            if ($sSqlOperator === 'like') {
                $sSqlValue = "%{$v}%";
            }
            
            if (in_array($sField, $this->aDateFields)) {
                $oQuery->whereDate($sField, $sSqlOperator, $sSqlValue);
            } else {
                $oQuery->where($sField, $sSqlOperator, $sSqlValue);
            }
            
        }
        
        $iLimit = (int) $oRequest->input('limit', $this->iLimit);
        $iLimit = max(1, $iLimit);
        $iLimit = min($this->iLimitMax, $iLimit);

        if (!empty($this->aFilterParams['ob'])) {
            $this->aOrderBy = $this->aFilterParams['ob'];
        }

        foreach ($this->aOrderBy as $aOrderBy) {
            $oQuery->orderBy($aOrderBy['sField'], $aOrderBy['sSort']);
        }
        
        $oPaginatedData = $oQuery->paginate($iLimit);
//        tt($oPaginatedData);
//        $oPaginatedData = $oQuery->paginate($iLimit, array_keys($this->aListFields));

        return response()->json([
            'iCode' => 200,
            'aData' => [
                'cList' => $oPaginatedData->items(),
                'aListField' => $this->getListFields(),
                'aFieldTypes' => $this->aFieldTypes,
                'aMap' => $this->getMapAll(),
                'aValueStyle'   => $this->aValueStyle,
                'aSelectField' => $this->aSelectFields,
                'aFilterParam' => $this->aFilterParams,
                'aCcField' => $this->aCcFields,
                'sCcLast' => $this->getCcLast(),
                'sPageName' => $this->sPageName,
                'aPager' => [
                    'aOrderBySchema'=> $this->getOrderBySchema(),
                    'aOrderBy'      => $this->getOrderBy(),
                    'iTotal'        => $oPaginatedData->total(),
                    'iLimit'        => min($this->iLimitMax, $iLimit),
                    'iCurrentPage'  => $oPaginatedData->currentPage(),
                    'iLastPage'     => $oPaginatedData->lastPage(),
                    'iPerPage'      => $oPaginatedData->perPage(),
                    'iPrevPage'     => max(1, $oPaginatedData->currentPage() - 1),
                    'iNextPage'     => min($oPaginatedData->lastPage(), $oPaginatedData->currentPage() + 1)
                ]
            ]
        ], 200);
        
    }
   
}
