from flask import Flask, render_template_string, request, jsonify, redirect, url_for, session
import time
import json
import hashlib

app = Flask(__name__)
app.secret_key = "typing_full_articles_2025_pro"

# ====================== 1~6 年级 完整整篇课文（无删减） ======================
ARTICLES = {
    # 一年级 ⭐
    "一年级-秋天【⭐】": """秋天来了，天气凉了。
一片片黄叶从树上落下来。
一群大雁往南飞，一会儿排成个人字，一会儿排成个一字。
啊！秋天来了。""",

    "一年级-小小的船【⭐】": """弯弯的月儿小小的船，
小小的船儿两头尖。
我在小小的船里坐，
只看见闪闪的星星蓝蓝的天。""",

    "一年级-江南【⭐】": """江南可采莲，莲叶何田田。
鱼戏莲叶间。
鱼戏莲叶东，鱼戏莲叶西，
鱼戏莲叶南，鱼戏莲叶北。""",

    "一年级-画【⭐】": """远看山有色，近听水无声。
春去花还在，人来鸟不惊。""",

    # 二年级 ⭐⭐
    "二年级-找春天【⭐⭐】": """春天来了！春天来了！
我们几个孩子，脱掉棉袄，冲出家门，奔向田野，去寻找春天。
春天像个害羞的小姑娘，遮遮掩掩，躲躲藏藏。
我们仔细地找啊，找啊。
小草从地下探出头来，那是春天的眉毛吧？
早开的野花一朵两朵，那是春天的眼睛吧？
树木吐出点点嫩芽，那是春天的音符吧？
解冻的小溪叮叮咚咚，那是春天的琴声吧？
春天来了！我们看到了她，我们听到了她，我们闻到了她，我们触到了她。
她在柳枝上荡秋千，在风筝尾巴上摇啊摇；
她在喜鹊、杜鹃嘴里叫，在桃花、杏花枝头笑。""",

    "二年级-村居【⭐⭐】": """草长莺飞二月天，拂堤杨柳醉春烟。
儿童散学归来早，忙趁东风放纸鸢。""",

    "二年级-咏柳【⭐⭐】": """碧玉妆成一树高，万条垂下绿丝绦。
不知细叶谁裁出，二月春风似剪刀。""",

    # 三年级 ⭐⭐⭐
    "三年级-荷花【⭐⭐⭐】": """清晨，我到公园去玩，一进门就闻到一阵清香。我赶紧往荷花池边跑去。
荷花已经开了不少了。荷叶挨挨挤挤的，像一个个碧绿的大圆盘。
白荷花在这些大圆盘之间冒出来。
有的才展开两三片花瓣儿。有的花瓣儿全展开了，露出嫩黄色的小莲蓬。
有的还是花骨朵儿，看起来饱胀得马上要破裂似的。
这么多的白荷花，一朵有一朵的姿势。
看看这一朵，很美；看看那一朵，也很美。
如果把眼前的一池荷花看作一大幅活的画，那画家的本领可真了不起。
我忽然觉得自己仿佛就是一朵荷花，穿着雪白的衣裳，站在阳光里。
一阵微风吹过来，我就翩翩起舞，雪白的衣裳随风飘动。
不光是我一朵，一池的荷花都在舞蹈。
风过了，我停止了舞蹈，静静地站在那儿。
蜻蜓飞过来，告诉我清早飞行的快乐。
小鱼在脚下游过，告诉我昨夜做的好梦。
过了一会儿，我才记起我不是荷花，我是在看荷花呢。""",

    "三年级-守株待兔【⭐⭐⭐】": """宋人有耕者。田中有株。兔走触株，折颈而死。
因释其耒而守株，冀复得兔。
兔不可复得，而身为宋国笑。""",

    # 四年级 ⭐⭐⭐⭐
    "四年级-观潮【⭐⭐⭐⭐】": """农历八月十八是一年一度的观潮日。我跟着妈妈来到海宁市的盐官镇，据说这里是观潮最好的地方。
我们随着观潮的人群，登上了海塘大堤。宽阔的钱塘江横卧在眼前。
江面很平静，越往东越宽，在雨后的阳光下，笼罩着一层蒙蒙的薄雾。
镇海古塔、中山亭和观潮台屹立在江边。远处，几座小山在云雾中若隐若现。
江潮还没有来，海塘大堤上早已人山人海。大家昂首东望，等着，盼着。
午后一点左右，从远处传来隆隆的响声，好像闷雷滚动。
顿时人声鼎沸，有人告诉我们，潮来了！
我们踮着脚往东望去，江面还是风平浪静，看不出有什么变化。
过了一会儿，响声越来越大，只见东边水天相接的地方出现了一条白线。
那条白线很快地向我们移来，逐渐拉长，变粗，横贯江面。
再近些，只见白浪翻滚，形成一堵两丈多高的水墙。
浪潮越来越近，犹如千万匹白色战马齐头并进，浩浩荡荡地飞奔而来；
那声音如同山崩地裂，好像大地都被震得颤动起来。
霎时，潮头奔腾西去，可是余波还在漫天卷地般涌来，江面上依旧风号浪吼。
过了好久，钱塘江才恢复了平静。看看堤下，江水已经涨了两丈来高了。""",

    "四年级-爬山虎的脚【⭐⭐⭐⭐】": """学校操场北边墙上满是爬山虎。我家也有爬山虎，从小院的西墙爬上去，在房顶上占了一大片地方。
爬山虎刚长出来的叶子是嫩红的，不几天叶子长大，就变成嫩绿的。
爬山虎的嫩叶不大引人注意，引人注意的是长大了的叶子。
那些叶子绿得那么新鲜，看着非常舒服。
叶尖一顺儿朝下，在墙上铺得那么均匀，没有重叠起来的，也不留一点儿空隙。
一阵风拂过，一墙的叶子就漾起波纹，好看得很。
以前我只知道这种植物叫爬山虎，可不知道它怎么能爬。
今年我注意了，原来爬山虎是有脚的。
爬山虎的脚长在茎上。茎上长叶柄的地方，反面伸出枝状的六七根细丝，每根细丝像蜗牛的触角。
细丝跟新叶子一样，也是嫩红的。这就是爬山虎的脚。""",

    # 五年级 ⭐⭐⭐⭐⭐
    "五年级-匆匆【⭐⭐⭐⭐⭐】": """燕子去了，有再来的时候；杨柳枯了，有再青的时候；桃花谢了，有再开的时候。
但是，聪明的，你告诉我，我们的日子为什么一去不复返呢？
是有人偷了他们罢：那是谁？又藏在何处呢？
是他们自己逃走了罢：现在又到了哪里呢？
我不知道他们给了我多少日子；但我的手确乎是渐渐空虚了。
在默默里算着，八千多日子已经从我手中溜去；
像针尖上一滴水滴在大海里，我的日子滴在时间的流里，没有声音，也没有影子。
我不禁头涔涔而泪潸潸了。
去的尽管去了，来的尽管来着；去来的中间，又怎样地匆匆呢？
早上我起来的时候，小屋里射进两三方斜斜的太阳。
太阳他有脚啊，轻轻悄悄地挪移了；我也茫茫然跟着旋转。
于是——洗手的时候，日子从水盆里过去；
吃饭的时候，日子从饭碗里过去；
默默时，便从凝然的双眼前过去。
我觉察他去的匆匆了，伸出手遮挽时，他又从遮挽着的手边过去。
天黑时，我躺在床上，他便伶伶俐俐地从我身上跨过，从我脚边飞去了。
等我睁开眼和太阳再见，这算又溜走了一日。
我掩着面叹息。但是新来的日子的影儿又开始在叹息里闪过了。""",

    "五年级-落花生【⭐⭐⭐⭐⭐】": """我们家的后园有半亩空地。
母亲说：“让它荒着怪可惜的，你们那么爱吃花生，就开辟出来种花生吧。”
我们姐弟几个都很高兴，买种，翻地，播种，浇水，没过几个月，居然收获了。
母亲说：“今晚我们过一个收获节，请你们的父亲也来尝尝我们的新花生，好不好？”
母亲把花生做成了好几样食品，还吩咐就在后园的茅亭里过这个节。
那晚上天色不大好。可是父亲也来了，实在很难得。
父亲说：“你们爱吃花生吗？”
我们争着答应：“爱！”
“谁能把花生的好处说出来？”
姐姐说：“花生的味儿美。”
哥哥说：“花生可以榨油。”
我说：“花生的价钱便宜，谁都可以买来吃，都喜欢吃。这就是它的好处。”""",
}

# ====================== 前端页面（最强防复制） ======================
HTML_PAGE = '''
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>小学语文整篇课文打字练习</title>
    <style>
        * {margin:0;padding:0;box-sizing:border-box;font-family:微软雅黑;}
        body {max-width:1200px;margin:40px auto;padding:0 20px;background:linear-gradient(135deg,#4facfe 0%,#00f2fe 100%);min-height:100vh;color:white;}
        
        /* ========== 防复制核心样式 ========== */
        * {
            -webkit-user-select: none !important;
            -moz-user-select: none !important;
            -ms-user-select: none !important;
            user-select: none !important;
        }
        .article {
            font-size:22px;line-height:1.8;color:#222;background:white;padding:25px;border-radius:12px;margin-bottom:20px;
            white-space:pre-wrap;
            -webkit-user-select: none;
            user-select: none;
            pointer-events: none; /* 禁止点击选中 */
        }
        input,textarea {
            -webkit-user-select: auto !important;
            user-select: auto !important;
        }
        /* ==================================== */

        .title {text-align:center;font-size:32px;margin-bottom:20px;}
        .user-info {text-align:right;margin-bottom:15px;font-size:18px;}
        .select-box {margin:20px 0;text-align:center;}
        select {padding:12px 20px;font-size:16px;border-radius:10px;border:none;width:400px;}
        .correct {color:#00c853;font-weight:bold;}
        .error {color:#d50000;font-weight:bold;background:#ffebee;}
        .controls {display:flex;justify-content:space-between;align-items:center;margin:20px 0;font-size:19px;}
        .btn {background:#fff;color:#007bff;border:none;padding:10px 22px;font-size:16px;border-radius:8px;cursor:pointer;margin:0 6px;font-weight:bold;}
        .input-area {width:100%;height:180px;font-size:20px;padding:18px;border-radius:12px;border:2px solid #fff;background:rgba(255,255,255,0.95);}
        .result,.ranking {background:white;color:#222;padding:25px;border-radius:12px;margin-top:20px;display:none;}
        .rank-item {padding:10px;border-bottom:1px solid #eee;}
        .login-box {background:white;color:#222;padding:40px;border-radius:15px;max-width:450px;margin:80px auto;text-align:center;}
        .login-box input {width:100%;padding:14px;margin:12px 0;font-size:16px;border-radius:8px;border:1px solid #ddd;}
    </style>
</head>
<body>

{% if not session.user %}
<div class="login-box">
    <h2>🔐 登录 / 注册</h2>
    <input type="text" id="username" placeholder="用户名">
    <input type="password" id="password" placeholder="密码">
    <button class="btn" onclick="login()">登录</button>
    <button class="btn" onclick="register()">注册</button>
    <p id="msg" style="color:red;margin-top:10px;"></p>
</div>
<script>
function login(){let u=document.getElementById('username').value,p=document.getElementById('password').value;fetch('/login',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({username:u,password:p})}).then(r=>r.json()).then(d=>d.success?location.reload():document.getElementById('msg').innerText=d.msg)}
function register(){let u=document.getElementById('username').value,p=document.getElementById('password').value;fetch('/register',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({username:u,password:p})}).then(r=>r.json()).then(d=>document.getElementById('msg').innerText=d.msg)}
</script>
{% else %}
<div class="user-info">欢迎，{{ session.user }}！<button class="btn" onclick="logout()">退出</button></div>
<h1 class="title">⌨️ 小学语文整篇课文打字练习</h1>
<div class="select-box">
    <label>选择整篇课文：</label>
    <select id="s" onchange="change()"></select>
</div>
<div class="article" id="article">{{ default_article }}</div>
<div class="controls">
    <div>⏱ 用时：<span id="time">0.00</span> 秒</div>
    <div><button class="btn" id="pause">暂停</button><button class="btn" onclick="location.reload()">重置</button></div>
</div>
<textarea class="input-area" id="input" placeholder="开始输入，正确绿色、错误红色"></textarea>
<div class="result" id="result"><h2>📊 成绩</h2><p>用时：<span id="t"></span></p><p>字数：<span id="c"></span></p><p>正确率：<span id="a"></span></p><p>速度：<span id="s"></span></p></div>
<div class="ranking" style="display:block"><h3>🏆 排行榜</h3><div id="rank"></div></div>

<script>
// ========== 防复制 JS ==========
document.oncontextmenu = function(){return false;} // 禁止右键
document.onkeydown = function(e){
    if(e.ctrlKey && (e.key === 'c' || e.key === 'v' || e.key === 'x' || e.key === 's')) return false;
    if(e.key === 'F12') return false;
}
window.onload = function(){document.onselectstart = function(){return false};}
// ===============================

const articles = {{ articles|tojson }};
const keys=Object.keys(articles);
const sel=document.getElementById('s');
keys.forEach(k=>{const o=document.createElement('option');o.value=k;o.innerText=k;sel.appendChild(o)});
function change(){const v=sel.value;document.getElementById('article').innerText=articles[v];location.reload()}
const art=document.getElementById('article');
const inp=document.getElementById('input');
let txt=art.innerText;
let st=0,run=false,pause=false,timer=null;

inp.addEventListener('input',()=>{
    if(!run){st=Date.now()/1000;run=true;start()}
    color();
    if(inp.value===txt)done()
});

function color(){
    let v=inp.value,h='';
    for(let i=0;i<txt.length;i++){
        if(i<v.length)h+=v[i]===txt[i]?'<span class="correct">'+txt[i]+'</span>':'<span class="error">'+txt[i]+'</span>';
        else h+=txt[i];
    }
    art.innerHTML=h;
}

function start(){timer=setInterval(()=>{if(!pause)document.getElementById('time').innerText=(Date.now()/1000-st).toFixed(2)},100)}
document.getElementById('pause').onclick=()=>{pause=!pause;this.innerText=pause?'继续':'暂停';inp.disabled=pause};

function done(){clearInterval(timer);inp.disabled=true;
fetch('/calculate',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({user_text:inp.value,original_text:txt,start_time:st})})
.then(r=>r.json()).then(d=>{
document.getElementById('result').style.display='block';
document.getElementById('t').innerText=d.use_time+' 秒';
document.getElementById('c').innerText=d.total_chars+' 字';
document.getElementById('a').innerText=d.accuracy+' %';
document.getElementById('s').innerText=d.speed+' 字/分钟';
save(d.speed);show()});
}

function save(sp){let l=JSON.parse(localStorage.getItem('r'))||[];l.push({u:'{{ session.user }}',s:sp,t:new Date().toLocaleString()});l=l.sort((a,b)=>b.s-a.s).slice(0,8);localStorage.setItem('r',JSON.stringify(l))}
function show(){let l=JSON.parse(localStorage.getItem('r'))||[];let h='';l.forEach((i,n)=>h+=`<div class="rank-item">第${n+1}名 ${i.u}：${i.s}字/分钟</div>`);document.getElementById('rank').innerHTML=h||'暂无成绩'}
function logout(){fetch('/logout').then(()=>location.reload())}
window.onload=show;
</script>
{% endif %}
</body>
</html>
'''

# ====================== 后端 ======================
users = {}

@app.route('/')
def index():
    default = list(ARTICLES.keys())[0]
    return render_template_string(HTML_PAGE, articles=ARTICLES, default_article=ARTICLES[default])

@app.route('/register', methods=['POST'])
def reg():
    d = request.get_json()
    u, p = d.get('username'), d.get('password')
    if u in users: return jsonify(msg='已存在')
    users[u] = hashlib.md5(p.encode()).hexdigest()
    return jsonify(msg='注册成功！请登录')

@app.route('/login', methods=['POST'])
def log():
    d = request.get_json()
    u, p = d.get('username'), d.get('password')
    if u not in users: return jsonify(success=False, msg='无此用户')
    if users[u] == hashlib.md5(p.encode()).hexdigest():
        session['user'] = u
        return jsonify(success=True)
    return jsonify(success=False, msg='密码错误')

@app.route('/logout')
def out():
    session.clear()
    return redirect('/')

@app.route('/calculate', methods=['POST'])
def calc():
    d = request.get_json()
    ut, ot, t = d.get('user_text',''), d.get('original_text',''), d.get('start_time',0)
    use = round(time.time()-t,2)
    cor = sum(1 for a,b in zip(ut,ot) if a==b)
    total = len(ot)
    acc = round(cor/total*100,2) if total else 0
    speed = round(len(ut)/use*60,2) if use else 0
    return jsonify(use_time=use, total_chars=total, accuracy=acc, speed=speed)

if __name__ == '__main__':
    app.run(debug=True)