<?php

namespace app\common\app\common\arw\adjfut\src\Model;
use think\model\Collection;
use app\common\arw\adjfut\src\Exception\ErrorMsg;

/**
 * 中间表关联
 *
 * @date 2022-12-29
 * @example
 * @author arw
 * @since 1.0.0
 */
class Pivot
{
    /**
     * 中间表
     *
     * @var string
     */
    private $pivotClass = '';
    /**
     * 主表
     *
     * @var string
     */
    private $masterClass = '';
    /**
     * 子表
     *
     * @var string
     */
    private $subClass = '';
    /**
     * 中间表主键
     *
     * @var string
     */
    private $pivotPk = '';
    /**
     * 主表主键
     *
     * @var string
     */
    private $masterPk = '';
    /**
     * 子表主键
     *
     * @var string
     */
    private $subPk = '';
    /**
     * 是否软删除
     *
     * @var boolean
     */
    private $softDelete = false;
    /**
     * 初始化
     *
     * @param string $pivotClass 中间表
     * @param string $masterClass 主表
     * @param string $subClass 子表
     * @date 2022-12-29
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function __construct(
        string $pivotClass,
        string $masterClass,
        string $subClass,
        bool $softDelete = false
    ) {
        $this->pivotClass = $pivotClass;
        $this->masterClass = $masterClass;
        $this->subClass = $subClass;
        $this->softDelete = $softDelete;

        $this->pivotPk = (new $pivotClass)->getPk();
        $this->masterPk = (new $masterClass)->getPk();
        $this->subPk = (new $subClass)->getPk();
    }

    /**
     * 绑定
     *
     * @param array|Collection $masters
     * @param array|Collection $subs
     * @param array $extra 中间表额外数据
     * @return void
     * @date 2022-12-29
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function bind($masters, $subs, array $extra = []): void
    {
        $this->_bind($masters, $subs, $extra, false);
    }

    /**
     * 解绑
     *
     * @param array|Collection $masters
     * @param array|Collection $subs
     * @return void
     * @date 2022-12-29
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function unbind(array $masters, array $subs): void
    {
        $masters = self::formatModel($masters, $this->masterClass);
        $subs = self::formatModel($subs, $this->subClass);

        $pivotClass = $this->pivotClass;

        $pivotPk = $this->pivotPk;
        $masterPk = $this->masterPk;
        $subPk = $this->subPk;

        foreach ($masters as $master) {
            // 查询已绑定的数据
            $subPks = $pivotClass::where([
                $masterPk => $master->$masterPk
            ])->column($subPk);
            foreach ($subs as $sub) {
                if (in_array($sub->$subPk, $subPks)) {
                    $pivotClass::where([
                        $subPk => $sub->$subPk,
                        $masterPk => $master->$masterPk
                    ])->field([
                        $pivotPk
                    ])->select()->delete();
                }
            }
        }
    }

    /**
     * 重新绑定
     *
     * @param array|Collection $masters
     * @param array|Collection $subs
     * @param array $extra 中间表额外数据
     * @return void
     * @date 2022-12-29
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function rebind($masters, $subs, array $extra = []): void
    {
        $this->_bind($masters, $subs, $extra, true);
    }

    /**
     * 格式化模型
     *
     * @param array|Collection $models
     * @param string $class
     * @return Collection
     * @date 2022-12-29
     * @example
     * @author arw
     * @since 1.0.0
     */
    private static function formatModel($models, string $class): Collection
    {
        if ($models instanceof Collection) {
            return $models;
        } else if (is_array($models)) {
            $Collection = new Collection();
            foreach ($models as $model) {
                if ($model instanceof $class) {
                } else if (is_string($model) || is_numeric($model)) {
                    $model = $class::find($model);
                }
                $Collection->push($model);
            }
            return $Collection;
        } else {
            throw new ErrorMsg("类型异常", 1);
        }
    }

    /**
     * 绑定数据
     *
     * @param array|Collection $masters
     * @param array|Collection $subs
     * @param array $extra
     * @param boolean $rebind
     * @return void
     * @date 2022-12-29
     * @example
     * @author arw
     * @since 1.0.0
     */
    private function _bind($masters, $subs, array $extra = [], bool $rebind = false)
    {
        $masters = self::formatModel($masters, $this->masterClass);
        $subs = self::formatModel($subs, $this->subClass);

        $pivotClass = $this->pivotClass;

        $softDelete = $this->softDelete;

        $pivotPk = $this->pivotPk;
        $masterPk = $this->masterPk;
        $subPk = $this->subPk;

        foreach ($masters as $master) {
            if ($rebind) {
                $pivots = $pivotClass::where([
                    [$masterPk, '=', $master->$masterPk],
                    [$subPk, 'not in', $subs->column($subPk)],
                ])->select();
                if (!$pivots->isEmpty()) {
                    // 重新绑定
                    $this->unbind([$master], $pivots->column($subPk));
                }
            }
            foreach ($subs as $sub) {
                $pivot = null;
                if ($softDelete) {
                    $pivot = $pivotClass::withTrashed()->where([
                        $subPk => $sub->$subPk,
                        $masterPk => $master->$masterPk
                    ])->find();
                } else {
                    $pivot = $pivotClass::where([
                        $subPk => $sub->$subPk,
                        $masterPk => $master->$masterPk
                    ])->find();
                }
                if (is_null($pivot) || $pivot->isEmpty()) {
                    $pivotClass::create($extra + [
                        $subPk => $sub->$subPk,
                        $masterPk => $master->$masterPk
                    ]);
                } else {
                    if ($softDelete) {
                        $pivot->restore();
                    }
                    if ($extra) {
                        $pivotClass::update($extra, [
                            $pivotPk => $pivot->$pivotPk
                        ]);
                    }
                }
            }
        }
    }
}
