<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Malaysia Speaks · What Malaysia Talks About</title>
<style>
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;background:#f5f5f0;color:#1a1a1a}
.header{background:#fff;border-bottom:1px solid #e8e8e0;padding:16px 24px;display:flex;align-items:center;justify-content:space-between;position:sticky;top:0;z-index:100}
.logo{font-size:15px;font-weight:600}
.logo span{color:#1D9E75}
.live{display:inline-flex;align-items:center;gap:5px;font-size:11px;padding:3px 10px;border-radius:20px;background:#e1f5ee;color:#0F6E56;font-weight:500}
.dot{width:6px;height:6px;border-radius:50%;background:#1D9E75;animation:pulse 2s infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.3}}
.main{max-width:1100px;margin:0 auto;padding:24px}
.metrics{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-bottom:24px}
.metric{background:#fff;border:1px solid #e8e8e0;border-radius:10px;padding:14px 16px}
.metric-label{font-size:11px;color:#888;text-transform:uppercase;letter-spacing:0.05em;margin-bottom:6px}
.metric-value{font-size:24px;font-weight:600}
.metric-sub{font-size:11px;color:#aaa;margin-top:3px}
.section{background:#fff;border:1px solid #e8e8e0;border-radius:12px;overflow:hidden;margin-bottom:20px}
.tabs{display:flex;border-bottom:1px solid #f0f0e8}
.tab{font-size:13px;padding:10px 18px;border:none;background:none;color:#999;cursor:pointer;border-bottom:2px solid transparent;margin-bottom:-1px;display:flex;align-items:center;gap:6px}
.tab:hover{color:#1a1a1a}
.tab.active-green{color:#1D9E75;border-bottom:2px solid #1D9E75;font-weight:500}
.tab.active-yellow{color:#BA7517;border-bottom:2px solid #EF9F27;font-weight:500}
.tab.active-blue{color:#0D47A1;border-bottom:2px solid #378ADD;font-weight:500}
.fire-dot{width:8px;height:8px;border-radius:50%;background:#EF9F27;animation:pulse 1s infinite}
.filter-bar{display:flex;gap:8px;padding:12px 18px;border-bottom:1px solid #f5f5f0;flex-wrap:wrap;align-items:center}
.filter-btn{font-size:12px;padding:4px 12px;border-radius:20px;border:1px solid #e0e0d8;background:#fff;color:#666;cursor:pointer;transition:all 0.15s}
.filter-btn.active{background:#1D9E75;color:#fff;border-color:#1D9E75}
.sort-select{font-size:12px;padding:5px 10px;border-radius:20px;border:1px solid #e0e0d8;background:#fff;color:#555;outline:none;cursor:pointer}
.search{font-size:12px;padding:5px 12px;border-radius:20px;border:1px solid #e0e0d8;background:#fff;color:#1a1a1a;width:180px;outline:none}
table{width:100%;border-collapse:collapse;font-size:13px}
th{font-size:11px;font-weight:500;color:#999;text-align:left;padding:10px 12px;border-bottom:1px solid #f0f0e8;text-transform:uppercase;letter-spacing:0.04em;white-space:nowrap;cursor:pointer;user-select:none}
th:hover{color:#555}
th.sorted{color:#1D9E75}
td{padding:11px 12px;border-bottom:1px solid #f8f8f4;vertical-align:middle}
tr:last-child td{border-bottom:none}
tr:hover td{background:#fafaf7}
.rank{font-size:14px;font-weight:600;color:#ccc;width:32px;text-align:center}
.rank.r1{color:#B8860B}.rank.r2{color:#888}.rank.r3{color:#8B4513}
.source-badge{display:inline-block;font-size:11px;padding:2px 8px;border-radius:4px;font-weight:500}
.post-title{font-size:13px;color:#1a1a1a;line-height:1.4;max-width:320px}
.post-title a{color:#1D9E75;font-size:11px;margin-left:4px;text-decoration:none}
.post-meta{display:flex;gap:10px;margin-top:4px;flex-wrap:wrap}
.meta-item{font-size:11px;display:flex;align-items:center;gap:3px}
.meta-label{color:#ddd;font-size:10px;text-transform:uppercase}
.time-pub{color:#1D9E75;font-weight:500}
.time-snap{color:#bbb}
.num{font-size:13px;font-weight:500;text-align:right;white-space:nowrap}
.vel-cell{text-align:right;white-space:nowrap}
.vel-high{font-size:13px;font-weight:600;color:#BA7517;background:#FFF8E1;padding:2px 8px;border-radius:4px}
.vel-mid{font-size:13px;font-weight:500;color:#0D47A1;background:#E3F2FD;padding:2px 8px;border-radius:4px}
.vel-low{font-size:13px;color:#999}
.bar-wrap{width:70px}
.bar-label{font-size:11px;font-weight:500;margin-bottom:3px}
.bar-bg{height:3px;background:#f0f0e8;border-radius:2px}
.bar-fill{height:3px;border-radius:2px}
.last-update{font-size:11px;color:#bbb;padding:10px 18px;text-align:right;border-top:1px solid #f8f8f4}
.loading{text-align:center;padding:40px;color:#bbb;font-size:13px}
.empty{text-align:center;padding:32px;color:#bbb;font-size:13px}
.fire-banner{background:#FFFDE7;border-bottom:1px solid #FFF176;padding:8px 18px;font-size:12px;color:#F57F17;display:flex;align-items:center;gap:8px}
</style>
</head>
<body>
<div class="header">
  <div class="logo">Malaysia Speaks <span>· What Malaysia Talks About</span></div>
  <div style="display:flex;align-items:center;gap:12px">
    <span style="font-size:11px;color:#bbb" id="nextFetch">—</span>
    <span class="live"><span class="dot"></span>LIVE</span>
  </div>
</div>
<div class="main">
  <div class="metrics" id="metrics"><div class="loading">Loading...</div></div>
  <div class="section">
    <div class="tabs">
      <button class="tab active-green" id="tab-today" onclick="switchTab('today',this)">Today</button>
      <button class="tab" id="tab-viral" onclick="switchTab('viral',this)"><span class="fire-dot"></span>Trending Now</button>
      <button class="tab" id="tab-all" onclick="switchTab('all',this)">All Time</button>
    </div>
    <div id="fireBanner" style="display:none" class="fire-banner">🔥 Videos with rapid growth in the past 15 minutes</div>
    <div class="filter-bar">
      <button class="filter-btn active" onclick="filterPlatform('all',this)">All</button>
      <button class="filter-btn" onclick="filterPlatform('youtube',this)">YouTube</button>
      <button class="filter-btn" onclick="filterPlatform('facebook',this)">Facebook</button>
      <button class="filter-btn" onclick="filterPlatform('tiktok',this)">TikTok</button>
      <select class="sort-select" id="sortSelect" onchange="renderTable()">
        <option value="likes" selected>Sort: Likes</option>
            <option value="views">Sort: Views</option>
        <option value="likes">Sort: Likes</option>
        <option value="comments">Sort: Comments</option>
        <option value="velocity">Sort: Velocity🔥</option>
        <option value="total">Sort: Total</option>
      </select>
      <input class="search" id="searchBox" placeholder="Filter keywords..." oninput="renderTable()">
      <input type="date" id="dateFilter" style="font-size:12px;padding:5px 10px;border-radius:20px;border:1px solid #e0e0d8;background:#fff;color:#555;outline:none;cursor:pointer;" onchange="renderTable()">
      <button class="filter-btn" onclick="document.getElementById('dateFilter').value='';renderTable()">All Dates</button>
    </div>
    <div id="tableWrap"><div class="loading">Loading...</div></div>
    <div class="last-update" id="lastUpdate">—</div>
  </div>
</div>
<script>
const API = "https://malaysiaspeaks.com";
let allData = [];
let currentPlatform = "all";
let currentTab = "today";
let countdown = 900;

const sourceColors = {
  "BBK Network":["#FFF3E0","#E65100"],
  "Keluar Sekejap":["#E8F5E9","#1B5E20"],
  "BFM Radio":["#E3F2FD","#0D47A1"],
  "Rafizi Ramli":["#F3E5F5","#6A1B9A"],
  "星洲日报":["#FFF8E1","#F57F17"],
  "中国报":["#E8EAF6","#283593"],
  "东方日报":["#E0F2F1","#004D40"],
  "8TV华语新闻":["#FCE4EC","#880E4F"],
  "百格Pocketimes":["#F1F8E9","#33691E"],
  "KiniTalk":["#FBE9E7","#BF360C"],
  "AXTNJI":["#EDE7F6","#4527A0"],
};

function fmtNum(n){if(!n)return"0";if(n>=1000000)return(n/1000000).toFixed(1)+"M";if(n>=1000)return(n/1000).toFixed(1)+"K";return String(n)}
function fmtTime(ts){if(!ts)return"—";const d=new Date(ts);return d.toLocaleString("zh-MY",{month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",hour12:false})}
function timeSince(ts){if(!ts)return"—";const diff=Math.floor((new Date()-new Date(ts))/60000);if(diff<1)return"Just now";if(diff<60)return diff+"mins ago";if(diff<1440)return Math.floor(diff/60)+"hrs ago";return Math.floor(diff/1440)+"days ago"}

async function loadStats(){
  try{
    const r=await fetch(API+"/api/stats");
    const d=await r.json();
    document.getElementById("metrics").innerHTML=`
      <div class="metric"><div class="metric-label">Channels</div><div class="metric-value">${d.total_pages}</div><div class="metric-sub">YouTube · FB · TikTok</div></div>
      <div class="metric"><div class="metric-label">Today's Posts</div><div class="metric-value">${fmtNum(d.today_posts)}</div><div class="metric-sub">${d.date}</div></div>
      <div class="metric"><div class="metric-label">Snapshots</div><div class="metric-value">${fmtNum(d.total_snapshots)}</div><div class="metric-sub">Every 15 mins</div></div>
      <div class="metric"><div class="metric-label">Top Views Today</div><div class="metric-value" style="color:#1D9E75">${fmtNum(d.top_channel.max_views||0)}</div><div class="metric-sub">${d.top_channel.name||"—"}</div></div>
    `;
  }catch(e){}
}

async function loadData(){
  try{
    let url;
    if(currentTab==="viral"){
      url=API+"/api/velocity?threshold=100&limit=200";
    } else {
      const days=currentTab==="today"?2:30;
      url=API+"/api/trending?limit=200&days="+days;
    }
    const r=await fetch(url);
    const d=await r.json();
    allData=d.data;
    const fetchTime = new Date(d.updated_at); const elapsed = Math.floor((new Date()-fetchTime)/1000); countdown = Math.max(0, 900-elapsed); document.getElementById("lastUpdate").textContent="All times in Malaysia Time (+08) · Server last fetch: "+fetchTime.toLocaleTimeString("en-MY");
    document.getElementById("fireBanner").style.display=currentTab==="viral"?"flex":"none";
    if(currentTab==="viral"){
      document.getElementById("sortSelect").value="velocity";
    }
    renderTable();
  }catch(e){
    document.getElementById("tableWrap").innerHTML='<div class="empty">Unable to connect API</div>';
  }
}

function renderTable(){
  const kw=document.getElementById("searchBox").value.toLowerCase();
  const sortBy=document.getElementById("sortSelect").value;
  let data=allData.filter(p=>{
    if(currentPlatform!=="all"&&p.platform!==currentPlatform)return false;
    if(kw&&!p.text.toLowerCase().includes(kw)&&!(p.source||"").toLowerCase().includes(kw))return false;
    const dateVal=document.getElementById("dateFilter").value;
    if(dateVal){const pd=(p.published_at||"").substring(0,10);if(pd!==dateVal)return false;}
    return true;
  });
  data.sort((a,b)=>{
    if(sortBy==="views")return(b.views||0)-(a.views||0);
    if(sortBy==="likes")return(b.likes||0)-(a.likes||0);
    if(sortBy==="comments")return(b.comments||0)-(a.comments||0);
    if(sortBy==="velocity")return(b.velocity||0)-(a.velocity||0);
    if(sortBy==="total")return((b.likes||0)+(b.comments||0))-((a.likes||0)+(a.comments||0));
    return 0;
  });
  if(data.length===0){document.getElementById("tableWrap").innerHTML='<div class="empty">No results found</div>';return}
  const maxVal=Math.max(...data.map(p=>sortBy==="velocity"?(p.velocity||0):(p.views||0)));
  let html='<table><thead><tr><th>#</th><th>Source</th><th>Title · Time</th><th style="text-align:right">Views</th><th style="text-align:right">Likes</th><th style="text-align:right">Comments</th><th style="text-align:right">Velocity🔥</th><th>Heat</th></tr></thead><tbody>';
  data.forEach((p,i)=>{
    const c=sourceColors[p.source]||["#f5f5f5","#555"];
    const val=sortBy==="velocity"?(p.velocity||0):(p.views||0);
    const pct=maxVal>0?Math.round(val/maxVal*100):0;
    const rc=i===0?"r1":i===1?"r2":i===2?"r3":"";
    const barColor=pct>60?"#E24B4A":pct>30?"#EF9F27":"#1D9E75";
    const vel=p.velocity||0;
    const velClass=vel>1000?"vel-high":vel>200?"vel-mid":"vel-low";
    html+=`<tr>
      <td><span class="rank ${rc}">${i+1}</span></td>
      <td>
      <span class="source-badge" style="background:${c[0]};color:${c[1]}">${p.source}</span><br>
      <span style="font-size:10px;padding:1px 6px;border-radius:3px;margin-top:3px;display:inline-block;background:${p.platform==='youtube'?'#FCE4EC':p.platform==='facebook'?'#E8F0FE':'#F3E5F5'};color:${p.platform==='youtube'?'#C62828':p.platform==='facebook'?'#1877F2':'#6A1B9A'}">${p.platform.toUpperCase()}</span>
    </td>
      <td>
        <div class="post-title">${p.text.substring(0,60)}${p.text.length>60?"…":""}<a href="${p.post_url}" target="_blank">↗</a></div>
        <div class="post-meta">
          <span class="meta-item"><span class="meta-label">Published</span>&nbsp;<span class="time-pub">${fmtTime(p.published_at)}</span></span>
          <span class="meta-item"><span class="meta-label">Snapshot</span>&nbsp;<span class="time-snap">${fmtTime(p.snapshot_at)}</span></span>
        </div>
      </td>
      <td class="num">${fmtNum(p.views)}</td>
      <td class="num">${fmtNum(p.likes)}</td>
      <td class="num">${fmtNum(p.comments)}</td>
      <td class="vel-cell"><span class="${velClass}">+${fmtNum(vel)}</span></td>
      <td><div class="bar-wrap"><div class="bar-bg"><div class="bar-fill" style="width:${pct}%;background:${barColor}"></div></div></div></td>
    </tr>`;
  });
  html+='</tbody></table>';
  document.getElementById("tableWrap").innerHTML=html;
}

function filterPlatform(p,el){
  currentPlatform=p;
  document.querySelectorAll(".filter-btn").forEach(b=>b.classList.remove("active"));
  el.classList.add("active");
  renderTable();
}

function switchTab(tab,el){
  currentTab=tab;
  document.querySelectorAll(".tab").forEach(b=>{b.className="tab"});
  if(tab==="today")el.className="tab active-green";
  else if(tab==="viral")el.className="tab active-yellow";
  else el.className="tab active-blue";
  loadData();
}

function updateCountdown(){
  countdown--;
  if(countdown<=0){countdown=900;loadData();loadStats()}
  const m=Math.floor(countdown/60);
  const s=countdown%60;
  document.getElementById("nextFetch").textContent="Next refresh "+m+":"+(s<10?"0"+s:s);
}

loadStats();
loadData();
setInterval(updateCountdown,1000);
</script>
</body>
</html>