<?php
namespace App\Domains\Virtual\Services;

use App\Dicts;
use App\Core\Service;
use Illuminate\Support\Facades\DB;
use App\Exceptions\NotExistException;
use App\Exceptions\NotAllowedException;
use App\Models\Virtual\PropertySetting;
use App\Exceptions\InvalidArgumentException;
use App\Domains\Virtual\Repositories\ProductRepository;
use App\Domains\Virtual\Repositories\PropertyRepository;
use App\Domains\Virtual\Repositories\OrderCardRepository;
use App\Domains\Virtual\Repositories\PropertySettingRepository;
use App\Domains\Virtual\Repositories\OrderCardPartitionRepository;

class PropertyService extends Service
{
    protected $productRepository;
    protected $propertyRepository;
    protected $propertySettingRepository;

    public static $property_types = [
        'vehicle_group' => '车型分组',
        'vehicle_type' => '车型选项',
        'vehicle' => '车辆类型',
        'company' => '公司类型',
        'platform' => '平台/API类型',
        'customer' => '客户类型',
        'package' => '套餐分类',
        'product' => '产品类型',
        'package_type' => '套餐类型',
        'province' => '省份设置',
        'agent' => '机构设置',
    ];

    public static $default = [
        'vehicle' => '',
        'commercial_vehicle' => '',
        'company' => '',
        'platform' => '',
        'customer' => '',
        'package' => '',
        'product' => '',
        'province' => null,
        'province_status' => 0,
        'agent' => null,
        'agent_status' => 0,
        'created_at' => null,
        'updated_at' => null,
    ];

    protected static $properties;

    /**
     * 构造函数
     *
     * @return void
     */
    public function __construct(ProductRepository $productRepository, PropertyRepository $propertyRepository, PropertySettingRepository $propertySettingRepository)
    {
        $this->productRepository = $productRepository;
        $this->propertyRepository = $propertyRepository;
        $this->propertySettingRepository = $propertySettingRepository;
    }

    /**
     * 配置设置查看
     *
     * @param array $conditions
     * @return void
     */
    public function settings($conditions = [])
    {
        $settings = $this->propertySettingRepository->getAll();

        $products = $settings['product'];

        foreach ($settings as $key => $setting) {
            if ($setting['name'] === 'package') {
                $values = $setting['value'];
                foreach ($values as $k => $value) {
                    foreach ($value as $i => $v) {
                        if (!in_array($v, $products)) {
                            unset($settings[$key]['value'][$k][$i]);
                            $settings[$key]['value'][$k] = array_values($settings[$key]['value'][$k]);
                        }
                    }
                }
            }
        }

        $vehicleTypes = $settings['vehicle_type'];

        foreach ($settings as $key => $setting) {
            if ($setting['name'] === 'vehicle') {
                $values = $setting['value'];
                foreach ($values as $k => $value) {
                    foreach ($value as $i => $v) {
                        if (!in_array($v, $vehicleTypes)) {
                            unset($settings[$key]['value'][$k][$i]);
                            $settings[$key]['value'][$k] = array_values($settings[$key]['value'][$k]);
                        }
                    }
                }
            }
        }

        if ($conditions['names']) {
            $conditions['names'] = array_wrap($conditions['names']);

            $settings = array_where($settings, function ($value) use ($conditions) {
                return in_array($value['name'], $conditions['names']);
            });
        };

        return $settings;
    }

    /**
     * 配置设置写入
     *
     * @param array $values
     * @return void
     */
    public function settingsStore(array $values)
    {
        $settings = $this->propertySettingRepository->getAll();

        foreach ($values as $key => $value) {
            if (!in_array($key, array_keys(self::$property_types))) {
                throw new NotExistException("分类{$key}不存在");
            }

            if ($key !== 'province' && $key !== 'package' && $key !== 'vehicle') {
                $value = array_values(array_unique(array_merge($value)));
                $value = array_map(function ($item) {
                    return is_string($item) ? trim($item) : $item;
                }, $value);
            }

            $values[$key] = $value;
        }

        if (isset($values['package'])) {
            $packages = $values['package'];
            $products = $values['product'];
            foreach ($packages as $k => $v) {
                if (!is_array($v)) {
                    throw new InvalidArgumentException('传入的产品参数错误');
                }

                foreach ($v as $m) {
                    if (!in_array($m, $products)) {
                        throw new NotExistException("产品({$m})未配置或已删除");
                    }

                    foreach ($packages as $i => $j) {
                        if ($i !== $k && in_array($m, $j)) {
                            throw new NotAllowedException('一个产品仅能归属一个套餐');
                        }
                    }
                }
            }
        }

        if (isset($values['vehicle'])) {
            $vehicles = $values['vehicle'];
            $vehicleTypes = $values['vehicle_type'];
            foreach ($vehicles as $k => $v) {
                if (!is_array($v)) {
                    throw new InvalidArgumentException('传入的车型配置错误');
                }

                foreach ($v as $m) {
                    if (!in_array($m, $vehicleTypes)) {
                        throw new NotExistException("车型({$m})未配置或已删除");
                    }

                    foreach ($vehicles as $i => $j) {
                        if ($i !== $k && in_array($m, $j)) {
                            throw new NotAllowedException('一个车型仅能归属一个分组');
                        }
                    }
                }
            }
        }

        $data = [];

        foreach ($values as $key => $value) {
            $data[] = [
                'name' => $key,
                'value' => json_encode($value, 256),
            ];
        }

        PropertySetting::upsert($data, 'name');

        $settings = $this->propertySettingRepository->getAll(true);

        return $settings;
    }

    /**
     * 客户套餐配置列表
     *
     * @return void
     */
    public function index($conditions = [])
    {
        $product = $this->productRepository->whereHas('package', function ($query) {
            $query->whereNull('deleted_at');
        })->whereHas('company', function ($query) {
            $query->whereNull('deleted_at');
        })->withConditions($conditions)->applyConditions()->get();

        $properties = $this->propertyRepository->whereHas('package', function ($query) {
            $query->whereNull('deleted_at');
        })->whereHas('company', function ($query) {
            $query->whereNull('deleted_at');
        })->withConditions($conditions)->applyConditions()->get()->keyBy(function ($item) {
            return $item->company_id . '_' . $item->package_id;
        })->toArray();

        $sells = app(OrderCardRepository::class)->selectRaw('company_id, package_id, count(*) as counts')
            ->groupBy(['company_id', 'package_id'])->get()->keyBy(function ($item) {
                return $item->company_id . '_' . $item->package_id;
            })->toArray();

        $actives = app(OrderCardRepository::class)->selectRaw('company_id, package_id, count(*) as counts')
            ->withConditions(['card_status' => 2])->groupBy(['company_id', 'package_id'])->get()->keyBy(function ($item) {
                return $item->company_id . '_' . $item->package_id;
            })->toArray();

        $carrierOperators = app(Dicts::class)->get('carrier_operator');

        $list = $product->map(function ($item) use ($properties, $sells, $actives, $carrierOperators) {
            $item = $item->toArray();
            $package = PackageService::load($item['package_id']);
            $item['company_name'] = CompanyService::load($item['company_id'])['name'] ?? '';
            $item['package_name'] = $package['name'] ?? '';
            $item['flows'] = $package['flows'] ?? 0;
            $item['carrier_operator'] = $package['carrier_operator'];
            $item['package_type'] = $package['package_type'];
            $item['carrier_operator_name'] = $carrierOperators[$item['carrier_operator']] ?? '未知';
            $property = $properties[$item['company_id'] . '_' . $item['package_id']];
            $sell = $sells[$item['company_id'] . '_' . $item['package_id']];
            $active = $actives[$item['company_id'] . '_' . $item['package_id']];
            $item['counts'] = $sell ? $sell['counts'] : 0;
            $item['actives'] = $active ? $active['counts'] : 0;
            return array_merge($item, $property ?: self::$default);
        });

        return $list;
    }

    /**
     * 客户套餐配置修改
     *
     * @param array $values
     * @return void
     */
    public function store(array $values)
    {
        if (! is_array(reset($values))) {
            $values = [$values];
        }

        $only = ['company_id', 'package_id', 'product', 'vehicle_type', 'company', 'platform', 'customer', 'province', 'agent'];
        $checks = ['product', 'vehicle_type', 'company', 'customer'];

        $settings = $this->propertySettingRepository->getAll();
        $agents = app(AgentService::class)->listGroupByCompanyId();

        $data = [];

        foreach ($values as $value) {
            if (!isset($value['company_id']) || !isset($value['package_id'])) {
                continue;
            }

            $value['platform'] = $value['platform'] ?: '';
            $value = array_only($value, $only);

            foreach ($value as $k => $v) {
                if (in_array($k, $checks) && empty($v)) {
                    throw new  InvalidArgumentException(self::$property_types[$k] . '值不能为空');
                }

                if (in_array($k, $checks) && !in_array($v, $settings[$k])) {
                    throw new  InvalidArgumentException(self::$property_types[$k] . '值不正确');
                }

                if (in_array($k, ['platform']) && !in_array($v, $settings[$k]) && ($v !== '')) {
                    throw new  InvalidArgumentException(self::$property_types[$k] . '值不正确');
                }
            }

            if (!is_null($value['province'])) {
                if (!empty(array_diff(array_keys($value['province']), $settings['province']))) {
                    throw new  InvalidArgumentException('省份配置不正确');
                }

                if (array_sum($value['province']) != 0 && sprintf("%.2f", array_sum($value['province'])) != 100) {
                    throw new  InvalidArgumentException('百分比配置不正确');
                }

                $value['province'] = json_encode($value['province'], 256);
            }

            if (!is_null($value['agent'])) {
                $itemAgents = $agents[$value['company_id']];

                $index = [];
                foreach ($value['agent'] as $agent) {
                    if (in_array($agent['company_id'] . '_' . $agent['platform'], $index)) {
                        throw new  InvalidArgumentException('机构平台重复配置');
                    } else {
                        array_push($index, $agent['company_id'] . '_' . $agent['platform']);
                    }

                    if (!in_array($agent['agent'], array_pluck($itemAgents, 'id'))) {
                        throw new  InvalidArgumentException('机构配置不正确');
                    }

                    if (!in_array($agent['platform'], array_keys($settings['platform']))) {
                        throw new  InvalidArgumentException('机构平台配置不正确');
                    }
                }

                if (array_sum(array_pluck($value['agent'], 'percentages')) != 0 && sprintf("%.2f", array_sum(array_pluck($value['agent'], 'percentages'))) != 100) {
                    throw new  InvalidArgumentException('机构百分比配置不正确');
                }

                $value['agent'] = json_encode($value['agent'], 256);
            }

            if (!$package = $this->getPackage($value['product'])) {
                throw new NotExistException('产品套餐关系未配置');
            }

            if (!$vehicle = $this->getVehicle($value['vehicle_type'])) {
                throw new NotExistException('车型分组关系未配置');
            }

            $value['package'] = $package;
            $value['vehicle'] = $vehicle;

            $value['created_at'] = date('Y-m-d H:i:s');
            $value['updated_at'] = date('Y-m-d H:i:s');

            $data[$value['company_id'] . '_' . $value['package_id']] = $value;
        }

        if (empty($data)) {
            throw new NotAllowedException('数据未修改');
        }

        $values = array_values($data);

        $this->propertyRepository->upsert($values, ['company_id', 'package_id']);

        $this->propertyRepository->forgetCached();

        return true;
    }

    protected function getPackage($product)
    {
        if (!$package = $this->package) {
            $settings = $this->propertySettingRepository->getAll();
            $packages = $settings['package'];
            $this->package = $package;
        }

        foreach ($packages as $key => $products) {
            if (in_array($product, $products)) {
                return $key;
            }
        }

        return null;
    }

    protected function getVehicle($vehicleType)
    {
        if (!$vehicle = $this->vehicle) {
            $settings = $this->propertySettingRepository->getAll();
            $vehicles = $settings['vehicle'];
            $this->vehicle = $vehicle;
        }

        foreach ($vehicles as $key => $vehicleTypes) {
            if (in_array($vehicleType, $vehicleTypes)) {
                return $key;
            }
        }

        return null;
    }

    public static function load($company_id, $package_id)
    {
        if (!self::$properties) {
            $properties = app(PropertyRepository::class)
                ->select(['company_id', 'package_id', 'product', 'package'])->get();

            self::$properties = $properties->keyBy(function ($item) {
                return $item['company_id'] . '_' . $item['package_id'];
            })->toArray();
        }

        return self::$properties[$company_id . '_' . $package_id];
    }
}
