<?php
if (!defined('APP_ROOT')) exit;

function friend_links_install(array $plugin): void
{
    $t = app_db_types();
    app_db_create_table('plugin_friend_links', "id {$t['id']},user_id {$t['uint']} NOT NULL,name {$t['string']} NOT NULL,url {$t['string']} NOT NULL,sort INTEGER NOT NULL DEFAULT 0,created_at {$t['uint']} NOT NULL,updated_at {$t['uint']} NOT NULL");
    app_db_create_index('idx_friend_links_sort', 'plugin_friend_links(sort DESC,id)');
    app_db_create_index('idx_friend_links_user', 'plugin_friend_links(user_id,created_at DESC,id DESC)');
}

function friend_links_uninstall(array $plugin, bool $keep_data = true): void
{
    if (!$keep_data) app_db_drop_table('plugin_friend_links');
}

function friend_links_normalize_url(string $url): string
{
    $url = trim($url);
    if ($url === '' || filter_var($url, FILTER_VALIDATE_URL) === false) err('链接地址无效');
    $parts = parse_url($url);
    $scheme = strtolower((string)($parts['scheme'] ?? ''));
    $host = strtolower((string)($parts['host'] ?? ''));
    if (!in_array($scheme, ['http', 'https'], true) || $host === '' || isset($parts['user']) || isset($parts['pass'])) err('链接地址无效');
    return cut($url, 255);
}

function friend_links_public_ip(string $ip): bool
{
    return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false;
}

function friend_links_public_resolve(string $url): array
{
    $parts = parse_url($url);
    if (!is_array($parts)) throw new RuntimeException('链接地址无效');
    $host = strtolower((string)($parts['host'] ?? ''));
    $scheme = strtolower((string)($parts['scheme'] ?? ''));
    if ($host === '' || !in_array($scheme, ['http', 'https'], true)) throw new RuntimeException('链接地址无效');
    $ips = [];
    if (filter_var($host, FILTER_VALIDATE_IP) !== false) {
        $ips[] = $host;
    } else {
        if (!function_exists('dns_get_record')) throw new RuntimeException('当前环境无法安全解析对方网站');
        foreach (@dns_get_record($host, DNS_A | DNS_AAAA) ?: [] as $record) {
            $ip = (string)($record['ip'] ?? $record['ipv6'] ?? '');
            if ($ip !== '') $ips[$ip] = $ip;
        }
        $ips = array_values($ips);
    }
    if (!$ips) throw new RuntimeException('无法解析对方网站');
    foreach ($ips as $ip) if (!friend_links_public_ip($ip)) throw new RuntimeException('不允许访问内网地址');
    $port = (int)($parts['port'] ?? ($scheme === 'https' ? 443 : 80));
    $addresses = implode(',', array_map(static fn(string $ip): string => str_contains($ip, ':') ? '[' . $ip . ']' : $ip, $ips));
    return [$host . ':' . $port . ':' . $addresses];
}

function friend_links_validate_target(string $url): array
{
    if (!function_exists('curl_init')) return [false, '服务器未启用链接验证'];
    try {
        $resolve = friend_links_public_resolve($url);
    } catch (Throwable $e) {
        return [false, cut($e->getMessage(), 120)];
    }
    $body = '';
    $too_large = false;
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => false,
        CURLOPT_FOLLOWLOCATION => false,
        CURLOPT_CONNECTTIMEOUT => 5,
        CURLOPT_TIMEOUT => 10,
        CURLOPT_USERAGENT => 'bbs1.org FriendLinks/1.0',
        CURLOPT_HTTPHEADER => ['Accept: text/html,application/xhtml+xml'],
        CURLOPT_ENCODING => '',
        CURLOPT_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS,
        CURLOPT_RESOLVE => $resolve,
        CURLOPT_WRITEFUNCTION => static function ($handle, string $chunk) use (&$body, &$too_large): int {
            if (strlen($body) + strlen($chunk) > 1048576) {
                $too_large = true;
                return 0;
            }
            $body .= $chunk;
            return strlen($chunk);
        },
    ]);
    $ok = curl_exec($ch);
    $status = (int)curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    $content_type = strtolower((string)curl_getinfo($ch, CURLINFO_CONTENT_TYPE));
    curl_close($ch);
    if ($too_large) return [false, '对方页面内容过大'];
    if ($ok === false || $status < 200 || $status >= 300) return [false, '无法访问对方网站'];
    if ($content_type !== '' && !str_contains($content_type, 'text/html') && !str_contains($content_type, 'application/xhtml')) return [false, '对方页面不是网页内容'];
    return [true, $body];
}

function friend_links_site_targets(): array
{
    $base = rtrim((string)setting('site_base_url', ''), '/');
    if ($base === '') $base = rtrim(base_url(), '/');
    $host = strtolower((string)(parse_url($base, PHP_URL_HOST) ?? ''));
    return array_values(array_filter(array_unique([$base, $host, preg_replace('/^www\./', '', $host)])));
}

function friend_links_verify_backlink(string $url): bool
{
    [$ok, $body] = friend_links_validate_target($url);
    if (!$ok) err((string)$body);
    $decoded = html_entity_decode((string)$body, ENT_QUOTES | ENT_HTML5, 'UTF-8');
    foreach (friend_links_site_targets() as $target) if ($target !== '' && stripos($decoded, $target) !== false) return true;
    return false;
}

function friend_links_submit_page(): void
{
    need_login();
    if (is_post_request()) {
        require_post();
        $name = post('name', 80);
        $url = friend_links_normalize_url(post('url', 255));
        if ($name === '') err('请填写网站名称');
        if (!friend_links_verify_backlink($url)) err('未检测到对方网站已添加本站链接');
        if ((int)val('SELECT COUNT(*) FROM plugin_friend_links WHERE url=?', [$url]) > 0) err('该链接已经存在');
        q('INSERT INTO plugin_friend_links(user_id,name,url,sort,created_at,updated_at) VALUES(?,?,?,0,?,?)', [uid(), $name, $url, now(), now()]);
        set_flash('友情链接添加成功');
        go(route_url('home'));
    }
    $body = '<div class="form-panel friend-links-submit"><h2>申请友情链接</h2><p>请先在您的网站添加本站链接，再提交验证。验证成功后会立即显示在首页。</p><form method="post">' . form_token() . input('网站名称', 'name', '', 'text', true) . input('链接地址', 'url', '', 'url', true, '请填写已放置本站友情链接的公开页面。（如有人机验证请暂时关闭）') . '<button type="submit" data-loading-text="验证中">验证并添加</button></form></div>';
    page('申请友情链接', shell_html($body, sidebar_stack_html([sidebar_user_card_html()])));
}

function friend_links_sidebar(mixed $value, array $ctx): mixed
{
    if (!is_array($value) || empty($ctx['is_home_first_page'])) return $value;
    $rows = q('SELECT name,url FROM plugin_friend_links ORDER BY sort DESC,id')->fetchAll();
    if (!$rows && !uid()) return $value;
    $html = '<div class="card sidebar-card friend-links-card"><div class="quick-wrap"><div class="quick-title friend-links-title"><span>友情链接</span>' . (uid() ? '<a href="' . h(route_url('friend_links_submit')) . '">申请</a>' : '') . '</div>';
    if ($rows) {
        $html .= '<ul class="quick-links friend-links-list">';
        foreach ($rows as $row) $html .= '<li><a href="' . h((string)$row['url']) . '" target="_blank" rel="noopener noreferrer"><span>' . h((string)$row['name']) . '</span></a></li>';
        $html .= '</ul>';
    } else {
        $html .= '<p class="friend-links-empty">暂无友情链接</p>';
    }
    $value[] = $html . '</div></div>';
    return $value;
}

function friend_links_admin_page(array $plugin): string
{
    need_admin();
    if (is_post_request()) {
        require_post();
        $action = (string)($_POST['friend_links_action'] ?? 'save');
        $link_id = max(0, (int)($_POST['link_id'] ?? 0));
        if ($action === 'delete') {
            q('DELETE FROM plugin_friend_links WHERE id=?', [$link_id]);
            set_flash('友情链接已删除');
            go(admin_url(['tab' => 'friend_links']));
        }
        $name = post('name', 80);
        $url = friend_links_normalize_url(post('url', 255));
        $sort = max(-1000000, min(1000000, (int)($_POST['sort'] ?? 0)));
        if ($name === '') err('请填写网站名称');
        if ($link_id > 0) {
            q('UPDATE plugin_friend_links SET name=?,url=?,sort=?,updated_at=? WHERE id=?', [$name, $url, $sort, now(), $link_id]);
        } else {
            q('INSERT INTO plugin_friend_links(user_id,name,url,sort,created_at,updated_at) VALUES(?,?,?,?,?,?)', [uid(), $name, $url, $sort, now(), now()]);
        }
        set_flash($link_id > 0 ? '友情链接已更新' : '友情链接已添加');
        go(admin_url(['tab' => 'friend_links']));
    }
    $edit_id = max(0, (int)($_GET['edit'] ?? 0));
    $edit = $edit_id > 0 ? one('SELECT * FROM plugin_friend_links WHERE id=?', [$edit_id]) : null;
    $form = '<div class="admin-list-panel plugin-manage-panel"><div class="plugin-panel-body"><h2>' . ($edit ? '编辑友情链接' : '添加友情链接') . '</h2><form method="post">' . form_token() . hidden_inputs(['friend_links_action' => 'save', 'link_id' => (int)($edit['id'] ?? 0)]) . input('网站名称', 'name', (string)($edit['name'] ?? ''), 'text', true) . input('链接地址', 'url', (string)($edit['url'] ?? ''), 'url', true, '管理员添加和修改时跳过反向链接验证。') . number_input('排序', 'sort', (int)($edit['sort'] ?? 0), -1000000, 1000000, true, '数字越大越靠前。') . '<button type="submit">保存</button></form></div></div>';
    $rows = q('SELECT l.*,u.username FROM plugin_friend_links l LEFT JOIN app_users u ON u.id=l.user_id ORDER BY l.sort DESC,l.id')->fetchAll();
    $html = $form . '<div class="admin-list-panel plugin-manage-panel"><ul class="admin-manage-list">';
    foreach ($rows as $row) {
        $ops = '<a class="button" href="' . h(admin_url(['tab' => 'friend_links', 'edit' => (int)$row['id']])) . '">编辑</a><form method="post">' . form_token() . hidden_inputs(['friend_links_action' => 'delete', 'link_id' => (int)$row['id']]) . '<button type="submit" class="danger" data-confirm="确定删除该友情链接？">删除</button></form>';
        $html .= '<li class="admin-list-item admin-object-row"><div class="admin-row-main"><a class="admin-content-title" href="' . h((string)$row['url']) . '" target="_blank" rel="noopener">' . h((string)$row['name']) . '</a><div class="admin-row-meta"><span>排序 ' . (int)$row['sort'] . '</span><span>提交人：' . h((string)($row['username'] ?? '管理员')) . '</span><span>' . h((string)$row['url']) . '</span></div></div><div class="admin-inline-ops">' . $ops . '</div></li>';
    }
    if (!$rows) $html .= '<li class="empty-state">暂无友情链接</li>';
    return $html . '</ul></div>';
}

function friend_links_css(): string
{
    return '.friend-links-card{order:999}.friend-links-title{display:flex;align-items:center;justify-content:space-between;gap:8px}.friend-links-title a{font-size:var(--font-size-sm);font-weight:400}.friend-links-list{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:0 10px;margin:0}.friend-links-list li{min-width:0;border-bottom:none}.friend-links-list a{min-width:0;color:var(--text-muted);font-size:var(--font-size-sm)}.friend-links-list a:before{flex:0 0 6px}.friend-links-list a>span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.friend-links-list a:hover{color:var(--brand)}.friend-links-empty{margin:0;color:var(--text-subtle);font-size:var(--font-size-sm)}.friend-links-submit>p{color:var(--text-muted)}@media(max-width:600px){.friend-links-list{grid-template-columns:repeat(2,minmax(0,1fr))}}';
}

return [
    'id' => 'friend_links',
    'name' => '友情链接',
    'version' => '1.1.0',
    'description' => '在首页展示友情链接，登录用户验证对方已添加本站后可自助申请，管理员可直接管理。',
    'author' => 'bbs1.org Team',
    'hooks' => ['sidebar.stack' => 'friend_links_sidebar'],
    'routes' => ['friend_links_submit' => 'friend_links_submit_page'],
    'admin_tabs' => ['friend_links' => 'friend_links_admin_page'],
    'assets' => ['css' => 'friend_links_css'],
    'install' => 'friend_links_install',
    'uninstall' => 'friend_links_uninstall',
];