<?php

namespace App\Services;

use App\Authority;
use App\OracleBasCustomer;
use App\Owner;
use App\OwnerPriceDirectLogistic;
use App\OwnerPriceExpress;
use App\OwnerPriceLogistic;
use App\OwnerPriceOperation;
use App\OwnerPriceSystem;
use App\OwnerStoragePriceModel;
use App\Services\common\BatchUpdateService;
use App\User;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use App\Traits\ServiceAppAop;


class OwnerService
{
    use ServiceAppAop;
    protected $modelClass=Owner::class;
    /** @var CacheService $cacheService */
    private $cacheService;
    function __construct(){
        $this->instant($this->cacheService,'CacheService');
    }
    /*
     * array | string $column
     * 默认一些select字段,可传递string 或 array来指定select字段
     */
    public function getIntersectPermitting(array $column = ['id', 'name'])
    {
        $ownerIds=app('UserService')->getPermittingOwnerIds(Auth::user());
        return $this->cacheService->getOrExecute('OwnersAll_IdName'.md5(json_encode($column).json_encode($ownerIds)),function()use($column,$ownerIds){
            if(empty($ownerIds))return new Collection();
            return Owner::query()->select($column)->whereIn('id', $ownerIds)->whereNull('deleted_at')->get();
        },config('cache.expirations.owners'));
    }

    public function getSelection($column = ['id'])
    {
        return $this->cacheService->getOrExecute('OwnersAll_'.md5(json_encode($column)),function()use($column){
            return Owner::filterAuthorities()->select($column)->get();
        },config('cache.expirations.owners'));
    }

    /**
     *同步WMS全部货主至WAS
     */
    public function syncOwnersData()
    {
        $basCustomers = OracleBasCustomer::query()
            ->select('CUSTOMERID', 'DESCR_C')
            ->where('DESCR_C', 'not like', '%换ERP%')
            ->where('DESCR_C', 'not like', '%退仓%')
            ->where('CUSTOMER_TYPE', 'OW')
            ->get();
        $ownerCount = Owner::query()->count();
        if (count($basCustomers) == $ownerCount) return null;
        foreach ($basCustomers as $basCustomer) {
            $owner = Owner::query()->where('code', $basCustomer['customerid'])->first();
            if (!isset($owner)){
                Owner::query()->create([
                    'code' => $basCustomer['customerid'],
                    'name' => $basCustomer['descr_c'],
                    'created_at' => Carbon::now()->format('Y-m-d H:i:s'),
                ]);
                continue;
            }
            if ($owner['name']!=$basCustomer['descr_c']){
                $owner->update([
                    'code' => $basCustomer['customerid'],
                    'name' => $basCustomer['descr_c'],
                ]);
            }
        }
        $owners = Owner::query()->select('id', 'name')->get();
        return $owners;
    }

    public function first(array $params, array $rules =[]){
        return $this->cacheService->getOrExecute('OwnersFirst'.md5(json_encode($params),json_encode($rules)),function()use($params,$rules){
            $owner = Owner::query();
            foreach ($params as $column => $value){
                if (!isset($rules[$column]))$owner->where($column, $value);
                else{
                    switch ($rules[$column]){
                        case "or":
                            $owner->orWhere($column, $value);
                            break;
                    }
                }
            }
            return $owner->first();
        },config('cache.expirations.rarelyChange'));
    }

    public function find($id, $with = [])
    {
        return Owner::query()->with($with)->find($id);
    }

    public function update(Owner $owner, array $values, array $related = [])
    {
        if ($related["ownerStoragePriceModels"] ?? false)$owner->ownerStoragePriceModels()->sync($related["ownerStoragePriceModels"]);
        return $owner->update($values);
    }

    public function create(array $params, array $related = []){
        /** @var Owner $owner */
        $owner = Owner::query()->create($params);
        if ($related["ownerStoragePriceModels"] ?? false)$owner->ownerStoragePriceModels()->syncWithoutDetaching($related["ownerStoragePriceModels"]);
        return $owner;
    }

    public function firstOrCreate(array $params, array $values = null){
        if (!$values) return Owner::query()->firstOrCreate($params);
        return Owner::query()->firstOrCreate($params,$values);
    }


    public function 获取订单跟踪的货主(){
        return Owner::query()->with('orderTrackingOwner')->whereHas('orderTrackingOwner',function($query){
            $query->where('status','启用');
        })->get();
    }

    public function getByWmsOrders($orderHeaders){
        $customerIds = array_unique(data_get($orderHeaders,'*.customerid'));
        $customerIds = array_diff($customerIds,[null,'','*']);
        $owners = Owner::query()->whereIn('code',$customerIds)->get();

        if($owners->count() < count($customerIds)){
            $customerIds = array_diff($customerIds,data_get($owners,'*.code'));
            $owner_list = $this->createByWmsCustomerIds($customerIds);
            $owners=$owners->concat($owner_list);
        }
        return $owners;
    }

    public function createByWmsCustomerIds($codes){
        if(!$codes) {return [];}
        $basCustomer = OracleBasCustomer::query()
            ->where('Customer_Type','OW')
            ->whereIn('CustomerID', $codes)
            ->get();

        $insert_params = [];
        $created_at = Carbon::now()->format('Y-m-d H:i:s');
        foreach ($basCustomer as $item) {
            $insert_params[] = [
                'code' => $item->customerid,
                'name' => $item->descr_c,
                'created_at' => $created_at,
            ];
        }

        try {
            if (count($insert_params) > 0) {
                $this->insert($insert_params);
                app('LogService')->log(__METHOD__, __FUNCTION__, '批量创建 owner ' . count($insert_params) . json_encode($insert_params) );
            }
        } catch (\Exception $e) {
            app('LogService')->log(__METHOD__, __FUNCTION__, '批量创建 owner error' . json_encode($insert_params) . '||' . $e->getMessage() . '||' . $e->getTraceAsString());
        } finally {
            return Owner::query()->whereIn('code', $codes)->get();
        }
    }

    public function insert($fillables){
        return Owner::query()->insert($fillables);
    }

    public function getAuthorizedOwners(){
        $user = Auth::user();
        return Owner::query()->whereIn('id',app('UserService')->getPermittingOwnerIds($user)??[])->get();
    }

    public function get(array $params, array $withs = null, bool $authority = true, bool $notShowSoftDelete = false, $user = null)
    {
        return Cache::remember(
            'owner_'.md5(json_encode($params).json_encode($withs).$authority.$notShowSoftDelete.json_encode($user))
            ,config('cache.expirations.rarelyChange')
            ,function()use($params,$withs,$authority,$notShowSoftDelete,$user){
            /** @var User $user */
            if ($user==null) {
                $user = Auth::user();
            }
            $query = Owner::query();
            if ($withs)$query->with($withs);
            if ($authority&&$user){
                $ids = $user->getPermittingOwnerIdsAttribute();
                $query->whereIn("id",$ids);
            }
            if ($notShowSoftDelete) $query->whereNull('deleted_at');
            $query = $this->query($query,$params);
            return $query->get();
        });
    }

    public function paginate(array $params, array $withs = null, bool $authority = true, bool $notShowSoftDelete = false)
    {
        /** @var User $user */
        $user = Auth::user();
        $query = Owner::query();
        if ($withs)$query->with($withs);
        if ($authority){
            $ids = $user->getPermittingOwnerIdsAttribute();
            $query->whereIn("id",$ids);
        }
        if ($notShowSoftDelete) $query->whereNull('deleted_at');
        $query = $this->query($query,$params)->orderByDesc("id");
        return $query->paginate($params["paginate"] ?? 50);
    }

    private function query(Builder $builder, array $params)
    {
        foreach ($params as $column => $param){
            if ($column == 'paginate' || $column == 'page' || !$param)continue;
            if ($param === true){
                $builder->whereNotNull($column);
                continue;
            }
            if ($param === false){
                $builder->whereNull($column);
                continue;
            }
            if ($column == 'created_at_start'){
                $builder->where("created_at",">=",$param.":00");
                continue;
            }
            if ($column == 'created_at_end'){
                $builder->where("created_at","<=",$param.":59");
                continue;
            }
            if ($column == 'contract_number'){
                $builder->whereHas("contracts",function ($query)use($param){
                    /** @var Builder $query */
                    $query->where("contract_number","like",$param."%");
                });
                continue;
            }
            if ($column == 'using_type'){
                $builder->whereHas("ownerStoragePriceModels",function ($query)use($param){
                    /** @var Builder $query */
                    $query->where("using_type",$param);
                });
                continue;
            }
            if (is_array($param))$builder->whereIn($column,$param);
            else $builder->where($column,$param);
        }
        return $builder;
    }

    public function getOwnerByCodes($codes)
    {
        $collect = collect();
        if(count($codes) == 0)return $collect;
        foreach ($codes as $code) {
            $collect = $collect->push($this->getOwnerByCode($code));
        }
        return $collect;
    }

    public function getOwnerByCode($code){
        return Cache::remember("getOwnerByCode_{$code}", config('cache.expirations.owners'), function ()use($code){
            $owner = Owner::query()->where('code',$code)->first();
            if($owner) return $owner;
             $basCustomer = app('OracleBasCustomerService')->first(['Customer_Type'=>'OW','CustomerID'=>$code]);
            if(!$basCustomer)return null;
            if($basCustomer && $basCustomer['active_flag']=='Y') return Owner::query()
                ->create(['name'=>$basCustomer['descr_c'],'code'=>$basCustomer['customerid']]);
            $deleted_at=Carbon::now()->toDateTimeString();
            if($basCustomer && $basCustomer['active_flag']=='N') return Owner::query()
                ->create(['name'=>$basCustomer['descr_c'],'code'=>$basCustomer['customerid'],'deleted_at'=>$deleted_at]);
        });
    }
    public function codeGetOwner($code)
    {
        return app(CacheService::class)->getOrExecute("owner_".$code,function ()use($code){
            return Owner::query()->firstOrCreate(["code"=>$code],["code"=>$code,"name"=>$code]);
        });
    }

    /**
     * 向FLUX同步推送WAS本地录入信息
     *
     * @param array|Owner|integer $owner
     * @return bool
     */
    public function syncPush($owner)
    {
        if (is_array($owner)){
            $owner = new Owner();
            foreach ($owner as $column=>$value){
                $owner[$column] = $value;
            }
        }
        if (is_numeric($owner)){
            $owner = Owner::query()->find($owner);
            if (!$owner)return false;
        }
        $wms = DB::connection("oracle")->selectOne(DB::raw("SELECT CUSTOMERID FROM BAS_CUSTOMER WHERE CUSTOMER_TYPE = ? AND CUSTOMERID = ?"),["OW",$owner->code]);
        if (!$wms && $owner->code){
            $query = DB::raw(<<<sql
    INSERT INTO BAS_CUSTOMER(CUSTOMERID,CUSTOMER_TYPE,DESCR_C,ADDTIME,EDITTIME,ADDWHO)
        VALUES(?,?,?,TO_DATE(?,'yyyy-mm-dd hh24:mi:ss'),TO_DATE(?,'yyyy-mm-dd hh24:mi:ss'),?)
sql
            );
            $date = date('Y-m-d H:i:s');
            DB::connection("oracle")->insert($query,[$owner->code,'OW',$owner->name,$date,$date,'WAS-'.(Auth::user() ? Auth::user()['name'] : 'SYSTEM')]);
        }
        return true;
    }


    public function syncUpdate($owner)
    {
        if (is_array($owner)){
            $owner = new Owner();
            foreach ($owner as $column=>$value){
                $owner[$column] = $value;
            }
        }
        if (is_numeric($owner)){
            $owner = Owner::query()->find($owner);
            if (!$owner)return false;
        }
        $sql = DB::raw(<<<sql
    update BAS_CUSTOMER set ACTIVE_FLAG = ?,EDITTIME = TO_DATE(?,'yyyy-mm-dd hh24:mi:ss'),EDITWHO = ? where CUSTOMERID = ? and CUSTOMER_TYPE = ?
sql
        );
        $date = date('Y-m-d H:i:s');
        if ($owner && $owner->deleted_at){
            DB::connection("oracle")->update($sql,['N',$date,'WAS-'.(Auth::user() ? Auth::user()['name'] : 'SYSTEM'),$owner->code,'OW']);
        }
       if ($owner && $owner->deleted_at==null) {
            DB::connection("oracle")->update($sql,['Y',$date,'WAS-'.(Auth::user() ? Auth::user()['name'] : 'SYSTEM'),$owner->code,'OW']);
        }
        return true;
    }

    /**
     * 同步货主时创建权限
     *
     * @param array|Owner $owner
     */
    public function createAuthority($owner)
    {
        Authority::query()->create([
            'name' => "_{$owner['id']}",
            'alias_name' => "(货主：{$owner['name']})",
            'remark' => "(key: _{$owner['id']})",
        ]);
    }

    /**
     * 停用货主时删除权限
     *
     * @param array|Owner $owner
     */
    public function deleteAuthority($owner)
    {
        $authorities = Authority::query()->where('name',"_{$owner['id']}")
            ->where("alias_name","like","(货主%")
            ->get(["id"]);
        $ids = array_column($authorities->toArray(),"id");
        DB::table("authority_role")->whereIn("id_authority",$ids)->delete();
        Authority::destroy($ids);
    }

    /**
     * 计费模型变动时更新货主中关联属性
     *
     * @param integer|array $owner
     * @param integer $type
     * @param bool $isDestroy
     *
     */
    public function refreshRelevance($owner, $type, $isDestroy = false)
    {
        if (!$owner)return;
        if (!is_array($owner))$owner = [$owner];
        $owners = Owner::query()->select("id","relevance")->whereIn("id",$owner)->get();
        $update = [["id","relevance"]];
        foreach ($owners as $ow){
            $relevance = $ow->relevance ?? [];
            $index = array_search($type,$relevance);
            $exist = $index===false ? false : true;
            if ($exist && $isDestroy && !$this->isExistModel($ow->id,$type)){
                array_splice($relevance,$index,1);
                $update[] = [
                    "id"=>$ow->id,
                    "relevance"=>$relevance,
                ];
            }
            if (!$exist && !$isDestroy){
                array_push($relevance,$type);
                $update[] = [
                    "id"=>$ow->id,
                    "relevance"=>$relevance,
                ];
            }
        }
        if (count($update)>1)app(BatchUpdateService::class)->batchUpdate("owners",$update);
    }

    private function isExistModel($owner, $type):bool
    {
        switch ($type){
            case 0:
                $table = "owner_storage_price_model_owner";
                break;
            case 1:
                $table = "owner_price_operation_owner";
                break;
            case 2:
                $table = "owner_price_logistic_owner";
                break;
            case 3:
                $table = "owner_price_express_owner";
                break;
            default:
                $table = "owner_price_direct_logistic_owner";
        }
        return DB::selectOne(DB::raw("SELECT 1 FROM {$table} WHERE owner_id = ? LIMIT 1"),[$owner]) ? true : false;
    }

    /**
     * 税率变更时附加税率
     *
     * @param Owner|\stdClass $owner
     */
    public function attachTaxRate(Owner $owner)
    {
        OwnerStoragePriceModel::query()->whereHas("owners",function (Builder $query)use($owner){
            $query->where("id",$owner->id);
        })->whereNull("tax_rate_id")->update(["tax_rate_id"=>$owner->tax_rate_id]);
        OwnerPriceOperation::query()->whereHas("owners",function (Builder $query)use($owner){
            $query->where("id",$owner->id);
        })->whereNull("tax_rate_id")->update(["tax_rate_id"=>$owner->tax_rate_id]);
        OwnerPriceExpress::query()->whereHas("owners",function (Builder $query)use($owner){
            $query->where("id",$owner->id);
        })->whereNull("tax_rate_id")->update(["tax_rate_id"=>$owner->tax_rate_id]);
        OwnerPriceLogistic::query()->whereHas("owners",function (Builder $query)use($owner){
            $query->where("id",$owner->id);
        })->whereNull("tax_rate_id")->update(["tax_rate_id"=>$owner->tax_rate_id]);
        OwnerPriceDirectLogistic::query()->whereHas("owners",function (Builder $query)use($owner){
            $query->where("id",$owner->id);
        })->whereNull("tax_rate_id")->update(["tax_rate_id"=>$owner->tax_rate_id]);
        OwnerPriceSystem::query()->where("owner_id",$owner->id)->whereNull("tax_rate_id")
            ->update(["tax_rate_id"=>$owner->tax_rate_id]);
    }

    /**
     * 税率变更时取消税率
     *
     * @param Owner|\stdClass $owner
     */
    public function removeTaxRate(Owner $owner)
    {
        OwnerStoragePriceModel::query()->whereHas("owners",function (Builder $query)use($owner){
            $query->where("id",$owner->id);
        })->update(["tax_rate_id"=>null]);
        OwnerPriceOperation::query()->whereHas("owners",function (Builder $query)use($owner){
            $query->where("id",$owner->id);
        })->update(["tax_rate_id"=>null]);
        OwnerPriceExpress::query()->whereHas("owners",function (Builder $query)use($owner){
            $query->where("id",$owner->id);
        })->update(["tax_rate_id"=>null]);
        OwnerPriceLogistic::query()->whereHas("owners",function (Builder $query)use($owner){
            $query->where("id",$owner->id);
        })->update(["tax_rate_id"=>null]);
        OwnerPriceDirectLogistic::query()->whereHas("owners",function (Builder $query)use($owner){
            $query->where("id",$owner->id);
        })->update(["tax_rate_id"=>null]);
        OwnerPriceSystem::query()->where("owner_id",$owner->id)
            ->update(["tax_rate_id"=>null]);
    }
}
