<?php
namespace Dipper\Foundation;

use Carbon\Carbon;
use Illuminate\Support\Str;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Dipper\Console\PackageManifest;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Http\Resources\Json\Resource;
use Symfony\Component\Debug\Exception\FatalThrowableError;
use Illuminate\Support\ServiceProvider as BaseServiceProvider;

class ServiceProvider extends BaseServiceProvider
{
    protected $providers = [
        \Barryvdh\Cors\ServiceProvider::class, // 跨域问题
        \Dipper\Foundation\LogServiceProvider::class, // 日志
        \Dipper\Foundation\Database\DatabaseServiceProvider::class,   // 数据库拓展
        \Dipper\Foundation\Debugger\DebuggererviceProvider::class,    // Debugger
        \Dipper\Foundation\Http\GuzzleServiceProvider::class,         // HTTP 客户端
        \Dipper\Foundation\Repository\EventServiceProvider::class,    // Repository 事件
        \Dipper\Foundation\Snowflake\SnowflakeServiceProvider::class, // Snowflake ID 生成
        \Dipper\Foundation\Nestedset\NestedSetServiceProvider::class, // 层级树
        \Dipper\Foundation\Purifier\PurifierServiceProvider::class // 防 XSS 跨站攻击
    ];

    protected $containerAliases = [
        'auth'             => ['Illuminate\Auth\AuthManage', 'Illuminate\Contracts\Auth\Factory'],
        'auth.driver'      => ['Illuminate\Contracts\Auth\Guard'],
        'cache'            => ['Illuminate\Cache\CacheManager', 'Illuminate\Contracts\Cache\Factory'],
        'cache.store'      => ['Illuminate\Cache\Repository', 'Illuminate\Contracts\Cache\Repository'],
        'redis'            => ['Illuminate\Redis\RedisManager', 'Illuminate\Contracts\Redis\Factory'],
        'filesystem'       => ['Illuminate\Filesystem\FilesystemManager', 'Illuminate\Contracts\Filesystem\Factory'],
        'filesystem.disk'  => ['Illuminate\Contracts\Filesystem\Filesystem'],
        'filesystem.cloud' => ['Illuminate\Contracts\Filesystem\Cloud'],
        'log'              => ['Illuminate\Log\Writer', 'Illuminate\Contracts\Logging\Log'],
    ];

    protected $classAliases = [
      'Jenssegers\Agent\Facades\Agent'           => 'Agent',
      'Dipper\Foundation\Http\Facades\Guzzle'    => 'Guzzle',
    ];

    /**
     * 注册时加载
     *
     * @return void
     */
    public function register()
    {
        $this->registerConfig();

        $this->app->singleton('filesystem', function ($app) {
            $filesystem = $app->loadComponent('filesystems', 'Illuminate\Filesystem\FilesystemServiceProvider', 'filesystem');
            return $filesystem;
        });

        foreach ($this->containerAliases as $key => $aliases) {
            foreach ($aliases as $alias) {
                $this->app->alias($key, $alias);
            }
        }
    }

    /**
     * 启动时加载
     *
     * @return void
     */
    public function boot()
    {
        $this->bootBindings();
        $this->bootMiddleware();
        $this->bootMacro();

        $this->bootValidator();

        if (app()->environment('local')) {
            $this->bootLocalBindings();
        }

        $this->bootPackageManifestProviders();
    }

    /**
     * 初始化配置
     *
     * @return void
     */
    public function registerConfig()
    {
        date_default_timezone_set('Asia/Shanghai');
        ini_set('default_charset', 'UTF-8');
        ini_set('error_reporting', E_ALL & ~E_NOTICE);
        // ini_set('memory_limit', '128m');
        define('TIMESTAMP', time());
        Carbon::setLocale('zh');
        Resource::withoutWrapping();

        $config = realpath(__DIR__.'/../config/dipper.php');
        $this->app->configure('dipper');
        $this->mergeConfigFrom($config, 'dipper');
    }

    /**
     * 服务注册绑定
     *
     * @return void
     */
    public function bootBindings()
    {
        $this->app->withFacades(true, $this->classAliases);
        $this->app->withEloquent();

        foreach ($this->providers as $provider) {
            $this->app->register($provider);
        }
    }

    /**
     * 中间件注册
     *
     * @return void
     */
    public function bootMiddleware()
    {
        $this->app->middleware([
            \Barryvdh\Cors\HandleCors::class,
            \Dipper\Foundation\Http\Middleware\TrimStrings::class,
            // \Dipper\Foundation\Http\Middleware\HtmlSpecialchars::class,
        ]);
    }

    /**
     * 注册扩展
     *
     * @return void
     */
    public function bootMacro()
    {
        Request::mixin(app(\Dipper\Foundation\Http\RequestMixin::class));

        Collection::macro('collect', function () {
            return $this->map(function ($value) {
                return collect((array) $value);
            });
        });
    }

    /**
     * Register all of the configured providers.
     *
     * @return void
     */
    public function bootPackageManifestProviders()
    {
        $manifest = new PackageManifest();

        $providers = $manifest->providers();

        if (empty($providers)) {
            $manifest->build();
            $providers = $manifest->providers();
        }

        foreach ($providers as $provider) {
            if (!class_exists($provider)) {
                $manifest->build();
                continue;
            }

            $this->app->register($provider);
        }
    }

    /**
     * 开发模式下加载
     *
     * @return void
     */
    public function bootLocalBindings()
    {
        $this->app->register(\Laravel\Tinker\TinkerServiceProvider::class);
        $this->app->register(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class);
    }


    /**
     * 注册验证规则.
     *
     * @return void
     */
    protected function bootValidator()
    {
        // 注册中国大陆手机号码验证规则
        $this->app->validator->extend('cn_phone', function (...$parameters) {
            return validate_china_phone_number($parameters[1]);
        });

        // 注册用户名验证规则
        $this->app->validator->extend('username', function (...$parameters) {
            return validate_username($parameters[1]);
        });

        // 注册显示长度验证规则
        $this->app->validator->extend('display_length', function ($attribute, string $value, array $parameters) {
            unset($attribute);

            return $this->validateDisplayLength($value, $parameters);
        });
    }

    /**
     * 验证显示长度计算.
     *
     * @param strint|int $value
     * @param array $parameters
     * @return bool
     */
    protected function validateDisplayLength(string $value, array $parameters): bool
    {
        if (empty($parameters)) {
            throw new \InvalidArgumentException('Parameters must be passed');
        // 补充 min 位.
        } elseif (count($parameters) === 1) {
            $parameters = [0, array_first($parameters)];
        }

        list($min, $max) = $parameters;

        preg_match_all('/[a-zA-Z0-9_]/', $value, $single);
        $length = count($single[0]) / 2 + mb_strlen(preg_replace('([a-zA-Z0-9_])', '', $value));

        return $length >= $min && $length <= $max;
    }
}
