<?php

declare(strict_types=1);

namespace app\admin\controller\split;

use app\admin\model\Domain as DomainModel;
use app\common\controller\Backend;
use app\common\library\CountryIso;
use app\common\service\SplitAutoReplyService;
use app\common\service\SplitLinkCodeService;
use app\common\service\SplitPixelConfigService;
use app\common\service\SplitPlatformDomainService;
use think\Db;
use think\Exception;
use think\exception\DbException;
use think\exception\PDOException;
use think\exception\ValidateException;
use think\response\Json;

/**
 * 链接管理
 *
 * @icon fa fa-link
 * @remark 分流链接管理，自动生成9位字母链接码
 */
class Link extends Backend
{
    /** @var \app\admin\model\split\Link */
    protected $model = null;

    protected $searchFields = 'link_code,description';

    protected $dataLimit = 'personal';

    protected $modelValidate = true;

    protected $modelSceneValidate = true;

    /** @var string[] 无需鉴权的操作 */
    protected $noNeedRight = ['script', 'copyinfo'];

    /** @var string patches 视图目录（未完整部署时使用） */
    private const PATCH_VIEW_DIR = 'patches/application/admin/view/split/link/';

    public function _initialize()
    {
        parent::_initialize();

        $lang = $this->request->langset();
        $lang = preg_match('/^([a-zA-Z\-_]{2,10})$/i', $lang) ? $lang : 'zh-cn';
        $langFile = ROOT_PATH . 'patches/application/admin/lang/' . $lang . '/split/link.php';
        if (is_file($langFile)) {
            \think\Lang::load($langFile);
        }

        $this->model = new \app\admin\model\split\Link();
        $countryList = CountryIso::getOptions();
        $this->view->assign('countryList', $countryList);
        $this->view->assign('ipProtectList', $this->model->getIpProtectList());
        $this->view->assign('randomShuffleList', $this->model->getRandomShuffleList());
        $this->view->assign('statusList', $this->model->getStatusList());
        $this->assignconfig('countryList', $countryList);
        $this->assignconfig('ipProtectList', $this->model->getIpProtectList());
        $this->assignconfig('randomShuffleList', $this->model->getRandomShuffleList());
        $this->assignconfig('statusList', $this->model->getStatusList());
        $this->assignconfig('linkFilterList', $this->buildLinkFilterList());

        $this->setupPatchFrontend();
    }

    /**
     * 分流链接列表筛选下拉（id => 链接码 - 描述）
     *
     * @return array<string, string>
     */
    private function buildLinkFilterList(): array
    {
        $query = $this->model->order('id', 'desc');
        if ($this->dataLimit) {
            $adminIds = $this->getDataLimitAdminIds();
            if (is_array($adminIds)) {
                $query->where('admin_id', 'in', $adminIds);
            }
        }
        $list = [];
        foreach ($query->field('id,link_code,description')->select() as $row) {
            $code = (string) $row['link_code'];
            $desc = (string) $row['description'];
            $label = $desc !== '' ? $code . ' - ' . $desc : $code;
            $list[(string) $row['id']] = $label;
        }
        return $list;
    }

    /**
     * 未部署 JS 到 public 时，或 patches 版本较新时，jsname 指向 script 接口
     */
    private function setupPatchFrontend(): void
    {
        $patchJs = ROOT_PATH . 'patches/public/assets/js/backend/split/link.js';
        $publicJs = ROOT_PATH . 'public/assets/js/backend/split/link.js';
        $usePatchJs = is_file($patchJs) && (
            !is_file($publicJs) || filemtime($patchJs) >= filemtime($publicJs)
        );
        if (!$usePatchJs) {
            return;
        }

        $cfg = is_array($this->view->config ?? null) ? $this->view->config : [];
        $version = (string) \think\Config::get('site.version');
        // 使用站内相对路径，避免生产环境 domain() 与反向代理/HTTPS 不一致导致 JS 跨域丢 Cookie
        $scriptUrl = (string) url('split.link/script', ['v' => $version], '', false);
        $cfg['jsname'] = $scriptUrl;
        $this->view->assign('config', $cfg);
        $this->view->config = $cfg;
    }

    /**
     * 渲染模板（优先 patches 视图，回退 application）
     */
    private function fetchPatch(string $template): string
    {
        $patchFile = ROOT_PATH . self::PATCH_VIEW_DIR . $template . '.html';
        $appFile = APP_PATH . 'admin/view/split/link/' . $template . '.html';
        if (is_file($patchFile)) {
            $file = $patchFile;
        } elseif (is_file($appFile)) {
            $file = $appFile;
        } else {
            $this->error('模板文件不存在');
        }
        return (string) $this->view->fetch($file);
    }

    /**
     * 列表
     *
     * @return string|Json
     * @throws DbException
     * @throws \think\Exception
     */
    public function index()
    {
        $this->request->filter(['strip_tags', 'trim']);
        if (false === $this->request->isAjax()) {
            return $this->fetchPatch('index');
        }
        if ($this->request->request('keyField')) {
            return $this->selectpage();
        }
        [$where, $sort, $order, $offset, $limit] = $this->buildparams();
        $list = $this->model
            ->where($where)
            ->order($sort, $order)
            ->paginate($limit);
        $result = ['total' => $list->total(), 'rows' => $list->items()];
        return json($result);
    }

    /**
     * 添加（打开表单时预生成分流链接，可编辑后保存）
     */
    public function add()
    {
        if (false === $this->request->isPost()) {
            try {
                $defaultLinkCode = (new SplitLinkCodeService())->generateUnique();
            } catch (Exception $e) {
                $this->error($e->getMessage());
            }
            $this->view->assign('defaultLinkCode', $defaultLinkCode);
            return $this->fetchPatch('add');
        }

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

        $params = $this->preExcludeFields($params);

        if (isset($params['link_code'])) {
            $params['link_code'] = SplitLinkCodeService::normalize((string) $params['link_code']);
        }

        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);
            }
            $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();
    }

    /**
     * 复制分流链接弹窗所需数据（平台域名 + 我的域名列表）
     *
     * @return Json
     */
    public function copyinfo(): Json
    {
        $platformDomains = $this->getPlatformDomains();

        $domainQuery = (new DomainModel())->order('id', 'desc');
        if ($this->dataLimit) {
            $adminIds = $this->getDataLimitAdminIds();
            if (is_array($adminIds)) {
                $domainQuery->where($this->dataLimitField, 'in', $adminIds);
            }
        }

        $myDomains = [];
        foreach ($domainQuery->select() as $domainRow) {
            $myDomains[] = [
                'id'              => (int) $domainRow['id'],
                'domain'          => (string) $domainRow['domain'],
                'ns_status'       => (string) $domainRow['ns_status'],
                'ns_status_text'  => (string) $domainRow['ns_status_text'],
                'dns_status'      => (string) $domainRow['dns_status'],
                'dns_status_text' => (string) $domainRow['dns_status_text'],
            ];
        }

        $this->success('', null, [
            'platform_domains' => $platformDomains,
            'my_domains'       => $myDomains,
            'domain_index_url' => (string) url('domain', '', false, false),
            'domain_add_url'   => (string) url('domain/add', '', false, false),
            'config_index_url' => (string) url('general/config/index'),
        ]);
    }

    /**
     * 读取平台分配域名列表（优先 site 配置，回退数据库）
     *
     * @return array<int, string>
     */
    private function getPlatformDomains(): array
    {
        $raw = trim((string) \think\Config::get('site.split_platform_domain'));
        if ($raw === '') {
            $raw = trim((string) Db::name('config')->where('name', 'split_platform_domain')->value('value'));
        }
        return SplitPlatformDomainService::parseList($raw);
    }

    /**
     * 自动回复：读取 / 保存与当前链接关联的回复语句
     *
     * @param string|null $ids 链接 ID
     * @return Json
     * @throws DbException
     */
    public function autoreply($ids = null): Json
    {
        $row = $this->model->get($ids);
        if (!$row) {
            $this->error(__('No Results were found'));
        }

        $adminIds = $this->getDataLimitAdminIds();
        if (is_array($adminIds) && !in_array((int) $row[$this->dataLimitField], $adminIds, true)) {
            $this->error(__('You have no permission'));
        }

        if ($this->request->isPost()) {
            $autoReply = (string) $this->request->post('auto_reply', '');
            $lines = SplitAutoReplyService::parseLines($autoReply);
            try {
                $row->save([
                    'auto_reply' => SplitAutoReplyService::formatStorage($lines),
                ]);
            } catch (PDOException|Exception $e) {
                $this->error($e->getMessage());
            }
            $this->success(__('Auto reply saved'));
        }

        $this->success('', null, [
            'id'         => (int) $row['id'],
            'link_code'  => (string) $row['link_code'],
            'auto_reply' => SplitAutoReplyService::formatDisplay((string) $row->getAttr('auto_reply')),
            'line_count' => count($row->getAutoReplyLines()),
        ]);
    }

    /**
     * 像素配置：读取 / 保存
     *
     * @param string|null $ids 链接 ID
     * @return Json
     * @throws DbException
     */
    public function pixel($ids = null): Json
    {
        $row = $this->model->get($ids);
        if (!$row) {
            $this->error(__('No Results were found'));
        }

        $adminIds = $this->getDataLimitAdminIds();
        if (is_array($adminIds) && !in_array((int) $row[$this->dataLimitField], $adminIds, true)) {
            $this->error(__('You have no permission'));
        }

        if ($this->request->isPost()) {
            $payload = $this->request->post('pixel_config/a', []);
            if ($payload === []) {
                $raw = (string) $this->request->post('pixel_config', '');
                $decoded = json_decode($raw, true);
                $payload = is_array($decoded) ? $decoded : [];
            }
            $existing = SplitPixelConfigService::parseStorage((string) $row->getAttr('pixel_config'));
            try {
                $merged = SplitPixelConfigService::mergeForSave($payload, $existing);
                $row->save([
                    'pixel_config' => SplitPixelConfigService::encodeStorage($merged),
                ]);
            } catch (\InvalidArgumentException $e) {
                $this->error($e->getMessage());
            } catch (PDOException|Exception $e) {
                $this->error($e->getMessage());
            }
            $this->success(__('Pixel config saved'));
        }

        $config = SplitPixelConfigService::parseStorage((string) $row->getAttr('pixel_config'));
        $masked = SplitPixelConfigService::maskForAdmin($config);

        $this->success('', null, [
            'id'            => (int) $row['id'],
            'link_code'     => (string) $row['link_code'],
            'facebook'      => $masked[SplitPixelConfigService::PLATFORM_FACEBOOK],
            'tiktok'        => $masked[SplitPixelConfigService::PLATFORM_TIKTOK],
            'event_options' => SplitPixelConfigService::EVENT_OPTIONS,
        ]);
    }

    /**
     * 编辑（分流链接码不可修改）
     *
     * @param string|null $ids
     * @return string
     * @throws DbException
     */
    public function edit($ids = null)
    {
        $row = $this->model->get($ids);
        if (!$row) {
            $this->error(__('No Results were found'));
        }

        $adminIds = $this->getDataLimitAdminIds();
        if (is_array($adminIds) && !in_array($row[$this->dataLimitField], $adminIds)) {
            $this->error(__('You have no permission'));
        }

        if (false === $this->request->isPost()) {
            $this->view->assign('row', $row);
            $this->view->assign('selectedCountries', $row->getSelectedCountries());
            return $this->fetchPatch('edit');
        }

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

        $params = $this->preExcludeFields($params);
        unset($params['link_code']);

        $result = false;
        Db::startTrans();
        try {
            if ($this->modelValidate) {
                $name = str_replace('\\model\\', '\\validate\\', get_class($this->model));
                $validate = $this->modelSceneValidate ? $name . '.edit' : $name;
                $row->validateFailException()->validate($validate, $params);
            }
            $result = $row->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 updated'));
        }

        $this->success();
    }

    /**
     * 输出后台列表/表单 JS（patches 未部署到 public 时由 RequireJS 加载）
     */
    public function script(): void
    {
        $jsFile = ROOT_PATH . 'patches/public/assets/js/backend/split/link.js';
        if (!is_file($jsFile)) {
            $jsFile = ROOT_PATH . 'public/assets/js/backend/split/link.js';
        }
        if (!is_file($jsFile)) {
            $this->error('脚本文件不存在');
        }
        $content = file_get_contents($jsFile);
        if ($content === false) {
            $this->error('读取脚本失败');
        }
        $response = response($content, 200, [
            'Content-Type' => 'application/javascript; charset=utf-8',
            'Cache-Control' => 'public, max-age=3600',
        ]);
        throw new \think\exception\HttpResponseException($response);
    }
}
