<?php

declare(strict_types=1);

namespace app\admin\model;

use think\Model;

/**
 * 域名管理模型
 */
class Domain extends Model
{
    /** @var string 中国域名后缀黑名单 */
    public const CHINA_SUFFIX_BLACKLIST = [
        'cn', 'com.cn', 'net.cn', 'org.cn', 'gov.cn', 'edu.cn', 'ac.cn', 'mil.cn',
    ];

    protected $name = 'domain';
    protected $autoWriteTimestamp = 'integer';
    protected $createTime = 'createtime';
    protected $updateTime = 'updatetime';
    protected $deleteTime = false;

    protected $append = [
        'zone_status_text',
        'ns_status_text',
        'dns_status_text',
    ];

    public function getZoneStatusList(): array
    {
        return [
            'pending' => __('Zone pending'),
            'active'  => __('Zone active'),
            'failed'  => __('Zone failed'),
        ];
    }

    public function getNsStatusList(): array
    {
        return [
            'pending'  => __('NS pending'),
            'verified' => __('NS verified'),
            'failed'   => __('NS failed'),
        ];
    }

    public function getDnsStatusList(): array
    {
        return [
            'pending' => __('DNS pending'),
            'created' => __('DNS created'),
            'failed'  => __('DNS failed'),
        ];
    }

    public function getZoneStatusTextAttr($value, $data): string
    {
        $value = $value ?: ($data['zone_status'] ?? '');
        $list = $this->getZoneStatusList();
        return $list[$value] ?? '';
    }

    public function getNsStatusTextAttr($value, $data): string
    {
        $value = $value ?: ($data['ns_status'] ?? '');
        $list = $this->getNsStatusList();
        return $list[$value] ?? '';
    }

    public function getDnsStatusTextAttr($value, $data): string
    {
        $value = $value ?: ($data['dns_status'] ?? '');
        $list = $this->getDnsStatusList();
        return $list[$value] ?? '';
    }

    public function setNameserversAttr($value): string
    {
        if (is_array($value)) {
            return json_encode(array_values($value), JSON_UNESCAPED_UNICODE);
        }
        return (string)$value;
    }

    /**
     * @return array<int, string>
     */
    public function getNameserversArray(): array
    {
        $value = $this->getAttr('nameservers');
        if (is_array($value)) {
            return $value;
        }
        if (is_string($value) && $value !== '') {
            $decoded = json_decode($value, true);
            return is_array($decoded) ? $decoded : [];
        }
        return [];
    }

    public static function normalizeDomain(string $domain): string
    {
        return strtolower(trim($domain));
    }

    public static function isValidRootDomain(string $domain): bool
    {
        $domain = self::normalizeDomain($domain);
        if ($domain === '' || strlen($domain) > 253) {
            return false;
        }
        if (strpos($domain, 'www.') === 0) {
            return false;
        }
        if (!preg_match('/^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/i', $domain)) {
            return false;
        }
        if (self::isChinaSuffix($domain)) {
            return false;
        }
        $label = self::getDomainLabelWithoutSuffix($domain);
        if ($label === '' || strpos($label, '.') !== false) {
            return false;
        }
        return true;
    }

    public static function isChinaSuffix(string $domain): bool
    {
        $domain = self::normalizeDomain($domain);
        foreach (self::CHINA_SUFFIX_BLACKLIST as $suffix) {
            if ($domain === $suffix || self::endsWith($domain, '.' . $suffix)) {
                return true;
            }
        }
        return false;
    }

    public static function getMatchedSuffix(string $domain): string
    {
        $domain = self::normalizeDomain($domain);
        $suffixes = array_merge(self::CHINA_SUFFIX_BLACKLIST, ['co.uk', 'com', 'net', 'org', 'io', 'co', 'me', 'info', 'biz']);
        usort($suffixes, function ($a, $b) {
            return strlen($b) - strlen($a);
        });
        foreach ($suffixes as $suffix) {
            if ($domain === $suffix || self::endsWith($domain, '.' . $suffix)) {
                return $suffix;
            }
        }
        return '';
    }

    public static function getDomainLabelWithoutSuffix(string $domain): string
    {
        $domain = self::normalizeDomain($domain);
        $suffix = self::getMatchedSuffix($domain);
        if ($suffix === '') {
            $pos = strrpos($domain, '.');
            return $pos === false ? $domain : substr($domain, 0, $pos);
        }
        $suffixWithDot = '.' . $suffix;
        if (self::endsWith($domain, $suffixWithDot)) {
            return substr($domain, 0, -strlen($suffixWithDot));
        }
        return $domain;
    }

    public static function mapCloudflareZoneStatus(string $status): string
    {
        if ($status === 'active') {
            return 'active';
        }
        if (in_array($status, ['pending', 'initializing'], true)) {
            return 'pending';
        }
        return 'failed';
    }

    private static function endsWith(string $haystack, string $needle): bool
    {
        if ($needle === '') {
            return true;
        }
        return substr($haystack, -strlen($needle)) === $needle;
    }
}
