<?php

declare(strict_types=1);

namespace app\admin\controller;

use app\admin\model\Domain as DomainModel;
use app\common\controller\Backend;
use app\common\service\CloudflareService;
use app\common\service\DomainDetectRateLimitService;
use think\Db;
use think\Exception;
use think\exception\PDOException;
use think\exception\ValidateException;

/**
 * 域名管理
 *
 * @icon fa fa-globe
 * @remark 域名接入Cloudflare，新增后请在详情查看NS并到注册商修改
 */
class Domain extends Backend
{
    /** @var DomainModel */
    protected $model = null;

    protected $searchFields = 'domain';
    protected $dataLimit = 'personal';
    protected $modelValidate = true;
    protected $modelSceneValidate = true;

    public function _initialize()
    {
        parent::_initialize();
        $this->model = new DomainModel();
        $this->view->assign('zoneStatusList', $this->model->getZoneStatusList());
        $this->view->assign('nsStatusList', $this->model->getNsStatusList());
        $this->view->assign('dnsStatusList', $this->model->getDnsStatusList());
        $this->assignconfig('zoneStatusList', $this->model->getZoneStatusList());
        $this->assignconfig('nsStatusList', $this->model->getNsStatusList());
        $this->assignconfig('dnsStatusList', $this->model->getDnsStatusList());
    }

    /**
     * 禁止编辑
     */
    public function edit($ids = null)
    {
        $this->error(__('Domain cannot be edited after creation'));
    }

    /**
     * 禁止批量操作
     */
    public function multi($ids = '')
    {
        $this->error(__('Invalid parameters'));
    }

    /**
     * 添加域名并创建 Cloudflare Zone
     */
    public function add()
    {
        if (false === $this->request->isPost()) {
            return $this->view->fetch();
        }

        $params = $this->request->post('row/a', []);
        if ($params === []) {
            $this->error(__('Parameter %s can not be empty', ''));
        }

        $params = $this->preExcludeFields($params);
        $domain = DomainModel::normalizeDomain((string)($params['domain'] ?? ''));
        $params['domain'] = $domain;

        if ($this->dataLimit && $this->dataLimitFieldAutoFill) {
            $params[$this->dataLimitField] = $this->auth->id;
        }

        $result = false;
        Db::startTrans();
        try {
            if ($this->modelValidate) {
                $name = str_replace('\\model\\', '\\validate\\', get_class($this->model));
                $validate = $this->modelSceneValidate ? $name . '.add' : $name;
                $this->model->validateFailException()->validate($validate, $params);
            }

            if (DomainModel::where('domain', $domain)->find()) {
                throw new ValidateException(__('Domain already exists'));
            }

            $cloudflare = new CloudflareService();
            $zone = $cloudflare->createZone($domain);

            $params['full_url'] = 'https://' . $domain;
            $params['zone_id'] = $zone['zone_id'];
            $params['nameservers'] = $zone['name_servers'];
            $params['zone_status'] = DomainModel::mapCloudflareZoneStatus($zone['status']);
            $params['ns_status'] = 'pending';
            $params['dns_status'] = 'pending';
            $params['check_result'] = '';

            $result = $this->model->allowField(true)->save($params);
            Db::commit();
        } catch (ValidateException $e) {
            Db::rollback();
            $this->error($e->getMessage());
        } catch (PDOException|Exception $e) {
            Db::rollback();
            $this->error($e->getMessage());
        }

        if ($result === false) {
            $this->error(__('No rows were inserted'));
        }

        $this->success(__('Domain submitted, please check NS in detail page and update at registrar'));
    }

    /**
     * 域名详情
     */
    public function detail($ids = null)
    {
        $row = $this->model->get($ids);
        if (!$row) {
            $this->error(__('No Results were found'));
        }
        if (!$this->checkDataLimit($row)) {
            $this->error(__('You have no permission'));
        }

        $data = $row->toArray();
        $data['nameservers_list'] = $row->getNameserversArray();
        $this->view->assign('row', $data);
        return $this->view->fetch();
    }

    /**
     * 检测域名状态
     */
    public function detect($ids = null)
    {
        if (!$this->request->isPost()) {
            $this->error(__('Invalid parameters'));
        }

        $row = $this->model->get($ids);
        if (!$row) {
            $this->error(__('No Results were found'));
        }
        if (!$this->checkDataLimit($row)) {
            $this->error(__('You have no permission'));
        }

        try {
            (new DomainDetectRateLimitService())->assertCanDetect((int)$this->auth->id, (int)$row['id']);
        } catch (Exception $e) {
            $this->error($e->getMessage());
        }

        try {
            $cloudflare = new CloudflareService();
            $detect = $cloudflare->detectDomain($row->toArray());
            $row->save([
                'zone_status'  => $detect['zone_status'],
                'ns_status'    => $detect['ns_status'],
                'dns_status'   => $detect['dns_status'],
                'check_time'   => time(),
                'check_result' => $detect['check_result'],
            ]);
        } catch (Exception $e) {
            $this->error($e->getMessage());
        }

        $this->success(__('Detection completed'), null, $row->toArray());
    }

    /**
     * 删除（需输入完整域名二次确认）
     */
    public function del($ids = '')
    {
        if (!$this->request->isPost()) {
            $this->error(__('Invalid parameters'));
        }

        $ids = $ids ?: $this->request->post('ids', '');
        $confirmDomain = trim((string)$this->request->post('confirm_domain', ''));
        if ($ids === '' || $confirmDomain === '') {
            $this->error(__('Please enter the full domain to confirm deletion'));
        }

        $pk = $this->model->getPk();
        $list = $this->model->where($pk, 'in', $ids)->select();
        if (!$list || count($list) === 0) {
            $this->error(__('No Results were found'));
        }

        $count = 0;
        Db::startTrans();
        try {
            foreach ($list as $item) {
                if (!$this->checkDataLimit($item)) {
                    throw new Exception(__('You have no permission'));
                }
                if (DomainModel::normalizeDomain($confirmDomain) !== DomainModel::normalizeDomain((string)$item['domain'])) {
                    throw new Exception(__('Domain confirmation does not match'));
                }

                if (!empty($item['zone_id'])) {
                    $cloudflare = new CloudflareService();
                    $cloudflare->deleteZone((string)$item['zone_id']);
                }

                $count += $item->delete();
            }
            Db::commit();
        } catch (PDOException|Exception $e) {
            Db::rollback();
            $this->error($e->getMessage());
        }

        if ($count > 0) {
            $this->success();
        }
        $this->error(__('No rows were deleted'));
    }

    /**
     * @param DomainModel $row
     */
    protected function checkDataLimit($row): bool
    {
        if (!$this->dataLimit) {
            return true;
        }
        $adminIds = $this->getDataLimitAdminIds();
        if (!is_array($adminIds)) {
            return true;
        }
        return in_array((int)$row[$this->dataLimitField], $adminIds, true);
    }
}
