<?php

require __DIR__ . '/../bootstrap.php';

use KeyLair\Auth;
use KeyLair\Database;
use KeyLair\DemoMask;
use KeyLair\Helpers;
use KeyLair\I18n;
use KeyLair\Platform;

Auth::requireLogin();
$pdo = Database::connection();

// campaign/product narrow every stat on this page, including the top
// three cards (previously left showing the whole install's inventory
// regardless of the filters, which read as broken rather than
// intentional -- filtering by product but seeing an unfiltered "Total
// keys" looks like the filter silently didn't apply to that card).
// days narrows only the trend chart's own window (the breakdowns below
// it stay all-time within the campaign/product filter, since "last 30
// days' worth of top creators" is a much less useful question than
// "all-time").
$campaignFilter = (int) ($_GET['campaign_id'] ?? 0);
$productFilter = (int) ($_GET['product_id'] ?? 0);
$daysOptions = [7, 14, 30, 90, 180, 365];
$days = (int) ($_GET['days'] ?? 30);
if (!in_array($days, $daysOptions, true)) {
    $days = 30;
}

$campaigns = $pdo->query('SELECT id, name FROM campaigns ORDER BY name ASC')->fetchAll();
$products = $pdo->query('SELECT id, name FROM products ORDER BY name ASC')->fetchAll();

$where = [];
$params = [];
if ($campaignFilter) {
    $where[] = 'cl.campaign_id = :campaign_id';
    $params['campaign_id'] = $campaignFilter;
}
if ($productFilter) {
    $where[] = 'r.product_id = :product_id';
    $params['product_id'] = $productFilter;
}
$whereSql = $where ? ('WHERE ' . implode(' AND ', $where)) : '';

// `keys` carries its own campaign_id/product_id directly, so the top
// cards' inventory counts can be scoped to the same filter without
// joining through redemptions/claim_links (a key isn't necessarily tied
// to a redemption at all -- most aren't, since most are still unclaimed).
$keysWhere = [];
$keysParams = [];
if ($campaignFilter) {
    $keysWhere[] = 'campaign_id = :campaign_id';
    $keysParams['campaign_id'] = $campaignFilter;
}
if ($productFilter) {
    $keysWhere[] = 'product_id = :product_id';
    $keysParams['product_id'] = $productFilter;
}
$keysWhereSql = $keysWhere ? ('WHERE ' . implode(' AND ', $keysWhere)) : '';

$totalKeysStmt = $pdo->prepare("SELECT COUNT(*) FROM `keys` {$keysWhereSql}");
$totalKeysStmt->execute($keysParams);
$totalKeys = (int) $totalKeysStmt->fetchColumn();

$redeemedWhereSql = implode(' AND ', array_merge($keysWhere, ["status='redeemed'"]));
$redeemedKeysStmt = $pdo->prepare("SELECT COUNT(*) FROM `keys` WHERE {$redeemedWhereSql}");
$redeemedKeysStmt->execute($keysParams);
$redeemedKeys = (int) $redeemedKeysStmt->fetchColumn();

$rate = $totalKeys > 0 ? round($redeemedKeys / $totalKeys * 100, 1) : 0;

$byPlatform = $pdo->prepare(
    "SELECT r.platform, COUNT(*) AS total
     FROM redemptions r JOIN claim_links cl ON cl.id = r.claim_link_id
     {$whereSql}
     GROUP BY r.platform ORDER BY total DESC"
);
$byPlatform->execute($params);
$byPlatform = $byPlatform->fetchAll();
$maxByPlatform = max(1, ...array_map(fn ($r) => (int) $r['total'], $byPlatform ?: [['total' => 0]]));

$byCreator = $pdo->prepare(
    "SELECT u.id AS user_id, u.name AS user_name, COUNT(*) AS total
     FROM redemptions r
     JOIN claim_links cl ON cl.id = r.claim_link_id
     LEFT JOIN users u ON u.id = cl.user_id
     {$whereSql}
     GROUP BY cl.user_id ORDER BY total DESC LIMIT 10"
);
$byCreator->execute($params);
$byCreator = $byCreator->fetchAll();
$maxByCreator = max(1, ...array_map(fn ($r) => (int) $r['total'], $byCreator ?: [['total' => 0]]));

$byProduct = $pdo->prepare(
    "SELECT p.name, COUNT(*) AS total
     FROM redemptions r JOIN claim_links cl ON cl.id = r.claim_link_id JOIN products p ON p.id = r.product_id
     {$whereSql}
     GROUP BY p.id ORDER BY total DESC LIMIT 10"
);
$byProduct->execute($params);
$byProduct = $byProduct->fetchAll();
$maxByProduct = max(1, ...array_map(fn ($r) => (int) $r['total'], $byProduct ?: [['total' => 0]]));

$byCampaign = $pdo->prepare(
    "SELECT c.name AS name, COUNT(*) AS total
     FROM redemptions r
     JOIN claim_links cl ON cl.id = r.claim_link_id
     LEFT JOIN campaigns c ON c.id = cl.campaign_id
     {$whereSql}
     GROUP BY c.id ORDER BY total DESC LIMIT 10"
);
$byCampaign->execute($params);
$byCampaign = $byCampaign->fetchAll();
// The "No campaign" bucket's label used to be baked into the SQL via
// COALESCE -- moved here so it can go through I18n::t() instead of being
// a hardcoded English string; behavior (grouping/ordering) is unchanged.
foreach ($byCampaign as &$byCampaignRow) {
    if ($byCampaignRow['name'] === null) {
        $byCampaignRow['name'] = I18n::t('analytics.no_campaign_label');
    }
}
unset($byCampaignRow);
$maxByCampaign = max(1, ...array_map(fn ($r) => (int) $r['total'], $byCampaign ?: [['total' => 0]]));

// Daily counts for the selected window, zero-filled -- grouping only
// returns days that actually had a redemption, so missing days are
// filled in here rather than left as gaps in the line below.
$dailyWhere = $where;
$dailyWhere[] = 'r.redeemed_at >= (CURDATE() - INTERVAL :range_days DAY)';
$dailyParams = $params;
$dailyParams['range_days'] = $days - 1;
$dailyMap = $pdo->prepare(
    "SELECT DATE(r.redeemed_at) AS d, COUNT(*) AS total
     FROM redemptions r JOIN claim_links cl ON cl.id = r.claim_link_id
     WHERE " . implode(' AND ', $dailyWhere) . "
     GROUP BY DATE(r.redeemed_at)"
);
$dailyMap->execute($dailyParams);
$dailyMap = $dailyMap->fetchAll(PDO::FETCH_KEY_PAIR);
$dailySeries = [];
for ($i = $days - 1; $i >= 0; $i--) {
    $date = date('Y-m-d', strtotime("-{$i} days"));
    $dailySeries[] = ['date' => $date, 'total' => (int) ($dailyMap[$date] ?? 0)];
}
$maxDaily = max(array_column($dailySeries, 'total'));

$pageTitle = I18n::t('analytics.page_title');
$activeNav = 'analytics';
require __DIR__ . '/includes/layout_header.php';
?>
<div class="topbar"><h1><?= Helpers::e(I18n::t('analytics.page_title')) ?></h1></div>

<form method="get" class="filter-row" style="display:flex; gap:10px; margin-bottom:16px; flex-wrap:wrap;">
  <select name="campaign_id" onchange="this.form.submit()" style="background:var(--bg-panel); border:1px solid var(--line); color:var(--text-primary); font-size:13px; padding:8px 12px; border-radius:8px;">
    <option value="0"><?= Helpers::e(I18n::t('analytics.filter_all_campaigns')) ?></option>
    <?php foreach ($campaigns as $c): ?>
      <option value="<?= (int) $c['id'] ?>" <?= $campaignFilter === (int) $c['id'] ? 'selected' : '' ?>><?= Helpers::e($c['name']) ?></option>
    <?php endforeach; ?>
  </select>
  <select name="product_id" onchange="this.form.submit()" style="background:var(--bg-panel); border:1px solid var(--line); color:var(--text-primary); font-size:13px; padding:8px 12px; border-radius:8px;">
    <option value="0"><?= Helpers::e(I18n::t('analytics.filter_all_products')) ?></option>
    <?php foreach ($products as $p): ?>
      <option value="<?= (int) $p['id'] ?>" <?= $productFilter === (int) $p['id'] ? 'selected' : '' ?>><?= Helpers::e($p['name']) ?></option>
    <?php endforeach; ?>
  </select>
  <select name="days" onchange="this.form.submit()" style="background:var(--bg-panel); border:1px solid var(--line); color:var(--text-primary); font-size:13px; padding:8px 12px; border-radius:8px;">
    <?php foreach ($daysOptions as $opt): ?>
      <option value="<?= $opt ?>" <?= $days === $opt ? 'selected' : '' ?>><?= Helpers::e(sprintf(I18n::t('analytics.filter_days_option'), $opt)) ?></option>
    <?php endforeach; ?>
  </select>
</form>

<div class="stat-grid" style="grid-template-columns: repeat(3, 1fr);">
  <div class="stat-card"><div class="stat-label"><?= Helpers::e(I18n::t('analytics.stat_total_keys')) ?></div><div class="stat-value"><?= number_format($totalKeys) ?></div></div>
  <div class="stat-card"><div class="stat-label"><?= Helpers::e(I18n::t('analytics.stat_redeemed')) ?></div><div class="stat-value gold"><?= number_format($redeemedKeys) ?></div></div>
  <div class="stat-card"><div class="stat-label"><?= Helpers::e(I18n::t('analytics.stat_redemption_rate')) ?></div><div class="stat-value signal"><?= $rate ?>%</div></div>
</div>

<div class="row-2 row-2-even">
  <div class="panel">
    <p class="panel-title"><?= Helpers::e(I18n::t('analytics.chart_by_platform')) ?></p>
    <?php if (empty($byPlatform)): ?><p style="color:var(--text-faint); font-size:13px;"><?= Helpers::e(I18n::t('analytics.no_data')) ?></p><?php endif; ?>
    <div class="bar-list">
    <?php foreach ($byPlatform as $r): ?>
      <div class="bar-row">
        <span class="bar-plat"><?= Helpers::e(Platform::label($r['platform'])) ?></span>
        <div class="bar-track"><div class="bar-fill" style="width:<?= round($r['total'] / $maxByPlatform * 100) ?>%"></div></div>
        <span class="bar-num"><?= (int) $r['total'] ?></span>
      </div>
    <?php endforeach; ?>
    </div>
  </div>

  <div class="panel">
    <p class="panel-title"><?= Helpers::e(I18n::t('analytics.chart_top_products')) ?></p>
    <?php if (empty($byProduct)): ?><p style="color:var(--text-faint); font-size:13px;"><?= Helpers::e(I18n::t('analytics.no_data')) ?></p><?php endif; ?>
    <div class="bar-list">
    <?php foreach ($byProduct as $r): ?>
      <div class="bar-row">
        <span class="bar-plat"><?= Helpers::e($r['name']) ?></span>
        <div class="bar-track"><div class="bar-fill" style="width:<?= round($r['total'] / $maxByProduct * 100) ?>%"></div></div>
        <span class="bar-num"><?= (int) $r['total'] ?></span>
      </div>
    <?php endforeach; ?>
    </div>
  </div>
</div>

<div class="row-2 row-2-even" style="margin-top:16px;">
  <div class="panel">
    <p class="panel-title"><?= Helpers::e(I18n::t('analytics.chart_top_creators')) ?></p>
    <?php if (empty($byCreator)): ?><p style="color:var(--text-faint); font-size:13px;"><?= Helpers::e(I18n::t('analytics.no_data')) ?></p><?php endif; ?>
    <div class="bar-list">
    <?php foreach ($byCreator as $r): ?>
      <div class="bar-row">
        <span class="bar-plat"><?= Helpers::e($r['user_name'] !== null ? DemoMask::name($r['user_name']) : sprintf(I18n::t('analytics.user_number'), (int) $r['user_id'])) ?></span>
        <div class="bar-track"><div class="bar-fill" style="width:<?= round($r['total'] / $maxByCreator * 100) ?>%"></div></div>
        <span class="bar-num"><?= (int) $r['total'] ?></span>
      </div>
    <?php endforeach; ?>
    </div>
  </div>

  <div class="panel">
    <p class="panel-title"><?= Helpers::e(I18n::t('analytics.chart_by_campaign')) ?></p>
    <?php if (empty($byCampaign)): ?><p style="color:var(--text-faint); font-size:13px;"><?= Helpers::e(I18n::t('analytics.no_data')) ?></p><?php endif; ?>
    <div class="bar-list">
    <?php foreach ($byCampaign as $r): ?>
      <div class="bar-row">
        <span class="bar-plat"><?= Helpers::e($r['name']) ?></span>
        <div class="bar-track"><div class="bar-fill" style="width:<?= round($r['total'] / $maxByCampaign * 100) ?>%"></div></div>
        <span class="bar-num"><?= (int) $r['total'] ?></span>
      </div>
    <?php endforeach; ?>
    </div>
  </div>
</div>

<?php
  // Hand-rolled inline SVG rather than a charting library -- one line,
  // zero dependencies, themed the same way every other chart on this page
  // already is (CSS custom properties work fine as SVG attribute values
  // when the SVG is inlined in the page, no different from any other
  // element).
  $chartW = 700; $chartH = 200;
  // padB is small now, not room for an axis label -- those render as
  // normal HTML text below the <svg> instead (see the markup below).
  $padL = 6; $padR = 6; $padT = 10; $padB = 8;
  $plotW = $chartW - $padL - $padR;
  $plotH = $chartH - $padT - $padB;
  $dayCount = count($dailySeries);
  $points = [];
  foreach ($dailySeries as $i => $pt) {
      $x = $padL + ($dayCount > 1 ? $i / ($dayCount - 1) * $plotW : $plotW / 2);
      $y = $padT + $plotH - ($maxDaily > 0 ? $pt['total'] / $maxDaily * $plotH : 0);
      $points[] = round($x, 1) . ',' . round($y, 1);
  }
  $polyline = implode(' ', $points);
  $areaPath = $dayCount > 0
      ? 'M' . $points[0] . ' L' . implode(' L', $points) . " L{$padL}," . ($padT + $plotH) . ' Z'
      : '';
?>
<div class="panel" style="margin-top:16px;">
  <p class="panel-title"><?= Helpers::e(sprintf(I18n::t('analytics.trend_title'), $days)) ?></p>
  <?php if ($maxDaily <= 0): ?>
    <p style="color:var(--text-faint); font-size:13px;"><?= Helpers::e(I18n::t('analytics.no_data')) ?></p>
  <?php else: ?>
  <!-- The date labels render as normal HTML text below the chart, not as
       SVG <text> inside it -- preserveAspectRatio="none" above stretches
       the viewBox non-uniformly to fill the panel's actual width, and an
       SVG <text> element stretches right along with it (its glyphs
       visibly warped wherever the horizontal and vertical scale factors
       differ, which they always do here since the chart is far wider
       than its viewBox's 700:200 ratio). HTML text outside that
       coordinate system is never subject to it, so it reads at its
       normal, undistorted size and weight, same as anywhere else on
       the page. -->
  <svg viewBox="0 0 <?= $chartW ?> <?= $chartH ?>" style="width:100%; height:200px; display:block;" preserveAspectRatio="none">
    <path d="<?= $areaPath ?>" fill="var(--signal)" opacity="0.12"></path>
    <polyline points="<?= $polyline ?>" fill="none" stroke="var(--signal)" stroke-width="2.5" stroke-linejoin="round" stroke-linecap="round" vector-effect="non-scaling-stroke"></polyline>
  </svg>
  <div style="display:flex; justify-content:space-between; margin-top:4px; font-size:11px; color:var(--text-faint); font-family:var(--font-mono);">
    <span><?= Helpers::e(date('M j', strtotime($dailySeries[0]['date']))) ?></span>
    <span><?= Helpers::e(date('M j', strtotime($dailySeries[(int) floor(($dayCount - 1) / 2)]['date']))) ?></span>
    <span><?= Helpers::e(date('M j', strtotime($dailySeries[$dayCount - 1]['date']))) ?></span>
  </div>
  <?php endif; ?>
</div>

<?php require __DIR__ . '/includes/layout_footer.php'; ?>
