<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <title>Fantasy Team Ranker — Daily/Weekly Updates</title>
  <meta name="viewport" content="width=device-width,initial-scale=1" />
  <style>
    body { font-family: system-ui, -apple-system, "Segoe UI", Roboto, Arial; margin: 18px; color:#111 }
    header { display:flex; gap:12px; align-items:center; margin-bottom:12px }
    h1 { font-size:20px; margin:0 }
    input[type=file] { display:inline-block }
    table { border-collapse:collapse; width:100%; margin-top:14px }
    th, td { border:1px solid #ddd; padding:8px; text-align:left }
    th { background:#f5f5f5 }
    .controls { display:flex; gap:10px; align-items:center; flex-wrap:wrap }
    .small { font-size:13px; color:#555 }
    .btn { padding:8px 10px; border-radius:6px; border:1px solid #bbb; background:white; cursor:pointer }
    .btn.primary { background:#0b79ff; color:white; border-color:#076ce6 }
    .team-row { cursor:pointer }
    pre { background:#fafafa; padding:10px; border-radius:6px; overflow:auto; max-height:220px }
    #playerBreakdown { margin-top:12px }
    .note { font-size:13px; color:#444; margin-top:8px }
  </style>
  <!-- SheetJS -->
  <script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js"></script>
</head>
<body>
  <header>
    <div>
      <h1>Fantasy Team Ranker — Daily & Weekly Updates</h1>
      <div class="small">Upload your team workbook (Excel). The app will parse all sheets and compute rankings using your scoring rules.</div>
    </div>
  </header>

  <div class="controls">
    <label class="small">Upload Excel:</label>
    <input id="fileInput" type="file" accept=".xlsx,.xls,.csv" />
    <button id="processBtn" class="btn primary">Process Now</button>

    <label class="small">Auto-refresh:</label>
    <select id="autoFreq" class="btn">
      <option value="0">Off</option>
      <option value="86400000">Daily (24h)</option>
      <option value="604800000">Weekly (7d)</option>
      <option value="600000">10 minutes (dev)</option>
    </select>

    <button id="exportCsv" class="btn">Export Rankings CSV</button>
    <div id="status" class="small" style="margin-left:8px"></div>
  </div>

  <section id="results">
    <table id="rankTable" style="display:none">
      <thead>
        <tr>
          <th>Rank</th><th>Team</th><th>Top 7 Total</th><th>Rest (50%)</th><th>Final Score</th>
        </tr>
      </thead>
      <tbody></tbody>
    </table>

    <div id="playerBreakdown"></div>
  </section>

  <div class="note">
    Notes:
    <ul>
      <li>Weights: PTS (PPG) ×2, AST ×1, 3PT ×1, STL ×1, BLK ×1.</li>
      <li>Top 7 players counted at full value; remaining roster players counted at 50%.</li>
      <li>If the workbook has missing 3PT column, the app will estimate 3PT per game using the rules you specified (G: ppg*0.12 capped, F: ppg*0.10 capped, C: ppg*0.05 capped).</li>
      <li>For true automatic daily updates from live stats you must supply an API endpoint/token and update the `fetchLiveStats()` stub below to return the same structured data.</li>
    </ul>
  </div>

<script>
/* -------------------------
   Utility: normalize headers
   ------------------------- */
function normalizeKey(k) {
  if (!k && k !== 0) return '';
  return String(k)
    .normalize('NFKD')            // remove weird spacing characters
    .replace(/\u00A0/g, ' ')      // non-breaking space
    .replace(/[^\x00-\x7F]/g, '') // drop non-ascii
    .trim()
    .toLowerCase()
    .replace(/\s+/g, '_');
}

/* -------------------------
   Estimation for 3PT
   ------------------------- */
function estimate3PT(pos, ppg){
  // pos: normalized position string (like "g" or "f,g")
  pos = (pos || '').toUpperCase();
  ppg = Number(ppg) || 0;
  if (pos.includes('G')) {
    return Math.max(1.5, Math.min(3.5, ppg * 0.12));
  } else if (pos.includes('F')) {
    return Math.max(1.0, Math.min(2.5, ppg * 0.10));
  } else if (pos.includes('C')) {
    return Math.max(0.3, Math.min(1.0, ppg * 0.05));
  } else {
    return Math.max(0.5, Math.min(2.0, ppg * 0.08));
  }
}

/* -------------------------
   Compute fantasy points
   ------------------------- */
function playerFantasyPoints(player) {
  // Expect fields: ppg, apg, spg, bpg, threept (if present), pos
  const ppg = Number(player.ppg) || 0;
  const apg = Number(player.apg) || 0;
  const spg = Number(player.spg) || 0;
  const bpg = Number(player.bpg) || 0;
  const threept = (player.threept !== undefined) ? Number(player.threept) : estimate3PT(player.pos, ppg);
  // weights: PTS x2, AST x1, 3PT x1, ST x1, BK x1
  const pts = ppg * 2 + apg + threept + spg + bpg;
  return { total: pts, breakdown: { ppg, apg, threept, spg, bpg } };
}

/* -------------------------
   Parse workbook -> teams structure
   ------------------------- */
function parseWorkbookToTeams(workbook) {
  const teams = [];
  workbook.SheetNames.forEach(sheetName => {
    const ws = workbook.Sheets[sheetName];
    const raw = XLSX.utils.sheet_to_json(ws, { defval: '' });
    if (raw.length === 0) return; // skip empty
    // normalize columns, build player objects
    const normalized = raw.map(row => {
      const out = {};
      for (const k in row) {
        const nk = normalizeKey(k);
        out[nk] = row[k];
      }
      // reasonable mapping guesses:
      // players -> players, pos -> pos, ppg -> ppg, apg -> apg, spg -> spg, bpg -> bpg, tpg or tpm -> threept
      return out;
    });

    // try to extract player rows with meaningful ppg or players column
    const players = normalized.filter(r => {
      const hasName = r.players && String(r.players).trim().length>0;
      const hasPPG = r.ppg !== undefined && String(r.ppg).trim() !== '';
      return hasName || hasPPG;
    }).map(r => {
      // cleanup: players might be "Name • TEAM" - strip team suffix
      const rawName = r.players || r.player || r.name || '';
      const name = String(rawName).split('•')[0].trim();
      return {
        name: name || 'Unknown',
        pos: r.pos || r.position || '',
        ppg: Number(r.ppg) || 0,
        apg: Number(r.apg) || 0,
        spg: Number(r.spg) || 0,
        bpg: Number(r.bpg) || 0,
        // some sheets might have a three pointer column named tpg/tpm/3pt etc
        threept: r.tpg || r.tpm || r["3pt"] || r.threept || undefined
      };
    });

    if (players.length) {
      teams.push({ team: sheetName, players });
    }
  });

  return teams;
}

/* -------------------------
   Ranking logic
   ------------------------- */
function rankTeams(teams) {
  const results = teams.map(team => {
    // compute fantasy points for each player
    const playersWithFP = team.players.map(p => {
      const fp = playerFantasyPoints(p);
      return Object.assign({}, p, { fantasy: fp.total, breakdown: fp.breakdown });
    });

    // sort desc by fantasy
    playersWithFP.sort((a,b) => b.fantasy - a.fantasy);

    // top 7 full value, rest 50%
    const top7 = playersWithFP.slice(0,7).reduce((s,p)=>s + p.fantasy, 0);
    const restHalf = playersWithFP.slice(7).reduce((s,p)=>s + p.fantasy * 0.5, 0);
    const finalScore = top7 + restHalf;

    return {
      team: team.team,
      players: playersWithFP,
      top7_total: Number(top7.toFixed(6)),
      rest_total_half: Number(restHalf.toFixed(6)),
      final_score: Number(finalScore.toFixed(6))
    };
  });

  results.sort((a,b) => b.final_score - a.final_score);
  // add rank
  return results.map((r,i) => Object.assign({ rank: i+1 }, r));
}

/* -------------------------
   UI actions
   ------------------------- */
let lastTeams = null;
let autoIntervalId = null;

function showStatus(msg) { document.getElementById('status').textContent = msg; }

function renderRankTable(ranked) {
  const table = document.getElementById('rankTable');
  const tbody = table.querySelector('tbody');
  tbody.innerHTML = '';
  ranked.forEach(r => {
    const tr = document.createElement('tr');
    tr.className = 'team-row';
    tr.dataset.team = r.team;
    tr.innerHTML = `<td>${r.rank}</td>
                    <td>${r.team}</td>
                    <td>${r.top7_total.toFixed(2)}</td>
                    <td>${r.rest_total_half.toFixed(2)}</td>
                    <td><strong>${r.final_score.toFixed(2)}</strong></td>`;
    // clicking row shows breakdown
    tr.addEventListener('click', () => showTeamBreakdown(r));
    tbody.appendChild(tr);
  });
  table.style.display = 'table';
}

function showTeamBreakdown(teamObj) {
  const out = document.getElementById('playerBreakdown');
  out.innerHTML = `<h3>Team: ${teamObj.team} — Rank ${teamObj.rank}</h3>`;
  const rows = teamObj.players.map((p, idx) => {
    const tri = `<tr>
      <td>${idx+1}</td>
      <td>${p.name}</td>
      <td>${p.pos}</td>
      <td>${(p.breakdown.ppg||0).toFixed(2)}</td>
      <td>${(p.breakdown.apg||0).toFixed(2)}</td>
      <td>${(p.breakdown.threept||0).toFixed(2)}</td>
      <td>${(p.breakdown.spg||0).toFixed(2)}</td>
      <td>${(p.breakdown.bpg||0).toFixed(2)}</td>
      <td><strong>${p.fantasy.toFixed(2)}</strong></td>
    </tr>`;
    return tri;
  }).join('');
  out.innerHTML += `<table style="margin-top:8px">
    <thead><tr><th>#</th><th>Name</th><th>Pos</th><th>PPG</th><th>APG</th><th>3PT (est)</th><th>STL</th><th>BLK</th><th>Fantasy</th></tr></thead>
    <tbody>${rows}</tbody></table>
    <div style="margin-top:8px">Top7 Total: <strong>${teamObj.top7_total.toFixed(2)}</strong> | Rest(50%): <strong>${teamObj.rest_total_half.toFixed(2)}</strong> | Final: <strong>${teamObj.final_score.toFixed(2)}</strong></div>`;
}

function onProcessWorkbook(workbook) {
  const teams = parseWorkbookToTeams(workbook);
  if (!teams.length) {
    showStatus('No valid teams found in workbook.');
    return;
  }
  lastTeams = teams;
  const ranked = rankTeams(teams);
  renderRankTable(ranked);
  showStatus(`Processed ${ranked.length} teams. Click a team for player breakdown.`);
}

/* -------------------------
   File input handling
   ------------------------- */
document.getElementById('processBtn').addEventListener('click', () => {
  const f = document.getElementById('fileInput').files[0];
  if (!f) { showStatus('Select an Excel file first.'); return; }
  const reader = new FileReader();
  reader.onload = (e) => {
    const data = e.target.result;
    let workbook;
    try {
      workbook = XLSX.read(data, { type: 'binary' });
    } catch (err) {
      showStatus('Error reading workbook: ' + err.message);
      return;
    }
    onProcessWorkbook(workbook);
  };
  reader.readAsBinaryString(f);
});

/* -------------------------
   Export CSV of rankings
   ------------------------- */
document.getElementById('exportCsv').addEventListener('click', () => {
  if (!lastTeams) { alert('No processed data to export.'); return; }
  const ranked = rankTeams(lastTeams);
  // build CSV
  let csv = 'Rank,Team,Top7,Rest_50pct,FinalScore\n';
  ranked.forEach(r => csv += `${r.rank},"${r.team}",${r.top7_total},${r.rest_total_half},${r.final_score}\n`);
  const blob = new Blob([csv], { type: 'text/csv' });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = 'fantasy_rankings.csv';
  a.click();
  URL.revokeObjectURL(url);
});

/* -------------------------
   Auto-refresh selector
   ------------------------- */
document.getElementById('autoFreq').addEventListener('change', (ev) => {
  const ms = Number(ev.target.value || 0);
  if (autoIntervalId) clearInterval(autoIntervalId);
  if (ms > 0) {
    // NOTE: This will simply re-run ranking on the last uploaded workbook.
    // For true daily live updates you'd plug in a server-side fetch of current stats.
    autoIntervalId = setInterval(() => {
      if (!lastTeams) { showStatus('No workbook uploaded yet for auto-refresh.'); return; }
      const ranked = rankTeams(lastTeams);
      renderRankTable(ranked);
      showStatus(`Auto-refreshed at ${new Date().toLocaleString()}`);
    }, ms);
    showStatus('Auto-refresh enabled.');
  } else {
    showStatus('Auto-refresh disabled.');
  }
});

/* -------------------------
   Stub: fetch live stats (server) - to be wired if you have an API
   ------------------------- */
/*
async function fetchLiveStats() {
  // Example (pseudo-code). Replace with your server endpoint that returns
  // the same structure expected by parseWorkbookToTeams => teams array:
  // [
  //   { team: "TeamName", players: [{ name, pos, ppg, apg, spg, bpg, threept }, ...] },
  //   ...
  // ]
  const resp = await fetch('https://your-server.example.com/fantasy_stats', { headers: { 'Authorization': 'Bearer ...' } });
  const json = await resp.json();
  // you would then call onProcessWorkbook or directly rankTeams(json)
}
*/

/* -------------------------
   Helpful: allow drag & drop
   ------------------------- */
window.addEventListener('dragover', (e)=>{ e.preventDefault(); });
window.addEventListener('drop', (e) => {
  e.preventDefault();
  const f = e.dataTransfer.files[0];
  if (!f) return;
  document.getElementById('fileInput').files = e.dataTransfer.files;
  showStatus('File dropped. Click "Process Now".');
});
</script>
</body>
</html>