#!/usr/bin/env python3
"""端到端测试：模拟用户在 UI 创建 ACTION 链接 → 访问链接 → 验证 Discord 发送 + 结果页。"""
import os
import sys
import re
import django

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings')
os.environ['DISCORD_WEBHOOK_URL'] = os.environ.get('DISCORD_WEBHOOK_URL', '')
if not os.environ['DISCORD_WEBHOOK_URL']:
    print('❌ 需要 DISCORD_WEBHOOK_URL 环境变量')
    sys.exit(1)

django.setup()

from django.test import Client
from django.contrib.auth import get_user_model
from unittest.mock import patch
from links.models import Link

# 创建/获取测试用户并登录（links 管理页需要认证）
User = get_user_model()
if not User.objects.filter(username='e2e_tester').exists():
    User.objects.create_user(username='e2e_tester', password='e2e_pass_123')
client = Client()
client.force_login(User.objects.get(username='e2e_tester'))

ALIAS = 'e2eask'

# 1. 打开创建页（links 的创建页在 /create/）
resp = client.get('/create/')
assert resp.status_code == 200, f'创建页 {resp.status_code}'
html = resp.content.decode()
assert '⚡ Action' in html, '缺少 Action tab'
assert 'id="action-tab"' in html, '缺少 action-tab 元素'
print('✅ 1. 创建页加载，Action tab 存在')

# 2. 提取 csrf 并模拟表单提交（Action 类型）
m = re.search(r'name="csrfmiddlewaretoken" value="([^"]+)"', html)
assert m, '找不到 csrf token'
csrf = m.group(1)

resp = client.post('/create/', {
    'csrfmiddlewaretoken': csrf,
    'alias': ALIAS,
    'link_type': 'ACTION',
    'action_type': 'discord_send',
    'action_message_template': '@小黑 {query}',
    'description': '端到端测试动作链接',
}, follow=True)
assert resp.status_code == 200, f'创建提交 {resp.status_code}'

link = Link.objects.get(alias=ALIAS)
assert link.link_type == 'ACTION', f'link_type={link.link_type}'
assert link.action_config == {'action_type': 'discord_send', 'message_template': '@小黑 {query}'}, link.action_config
print('✅ 2. 表单创建成功，action_config 正确保存:', link.action_config)

# 3. 编辑页预填验证
resp = client.get(f'/link/{link.pk}/edit/')
html = resp.content.decode()
assert 'value="discord_send"' in html or 'discord_send' in html, '编辑页未预填 action_type'
assert '@小黑 {query}' in html, '编辑页未预填 message template'
print('✅ 3. 编辑页正确预填动作配置')

# 4. 访问动作链接 → 发 Discord + 302 跳转（不渲染页面）
with patch('links.actions.send_discord_message') as mock_send:
    resp = client.get(f'/{ALIAS}/你好墨尔本/')
assert resp.status_code == 302, f'应 302 跳转: {resp.status_code}'
assert resp['Location'].startswith('https://discord.com/channels/'), resp['Location']
mock_send.assert_called_once_with('@小黑 你好墨尔本')
print('✅ 4. 访问链接 302 跳转 Discord，消息已发送（不发页面）')

# 4b. 防重：10 分钟内同一问题刷新不重复发送
with patch('links.actions.send_discord_message') as mock_send:
    resp = client.get(f'/{ALIAS}/你好墨尔本/')  # 刷新/重复访问
assert resp.status_code == 302
mock_send.assert_not_called(), '防重窗口内不应再次发送'
print('✅ 4b. 10 分钟防重生效：刷新不重复发送')

# 5. 无参数访问 → 提示缺参数，不发送空消息
with patch('links.actions.send_discord_message') as mock_send:
    resp = client.get(f'/{ALIAS}/')
assert resp.status_code == 200
assert '链接缺少参数' in resp.content.decode(), '缺少参数提示未出现'
mock_send.assert_not_called(), '空参数不应发送消息'
print('✅ 5. 无参数访问提示缺参数，不发送空消息')

# 6. 表单校验：ACTION 缺模板 → 报错
resp = client.post('/create/', {
    'csrfmiddlewaretoken': csrf,
    'alias': 'badaction',
    'link_type': 'ACTION',
    'action_type': 'discord_send',
    'action_message_template': '',
}, follow=True)
assert 'Message template is required' in resp.content.decode(), '缺少模板未报错'
print('✅ 6. ACTION 缺模板正确报错')

# 7. 普通链接不受影响
resp = client.post('/create/', {
    'csrfmiddlewaretoken': csrf,
    'alias': 'e2eplain',
    'link_type': 'LINK',
    'original_url': 'https://example.com',
}, follow=True)
assert resp.status_code == 200
plain = Link.objects.get(alias='e2eplain')
assert plain.action_config == {}, f'普通链接 action_config 应为空: {plain.action_config}'
print('✅ 7. 普通链接创建正常，action_config 清空')

# 清理测试数据
Link.objects.filter(alias__in=['e2eask', 'e2eplain']).delete()
print('\n🎉 端到端测试全部通过！检查 Discord #general 应该有一条 @小黑 你好墨尔本')
