<?php

declare(strict_types=1);

require_once dirname(__DIR__, 2) . '/core/auth.php';
require_once dirname(__DIR__, 2) . '/core/permissions.php';

function analytics_module_boot(): void
{
    require_login();

    if (!is_super_admin()) {
        abort_request(403, 'Access denied', 'Only Super Admin accounts can access site analytics.');
    }
}

function analytics_tracking_is_supported(): bool
{
    static $supported = null;

    if ($supported !== null) {
        return $supported;
    }

    try {
        $supported = db_fetch_value("SHOW TABLES LIKE 'site_visits'", [], '') !== '';
    } catch (Throwable) {
        $supported = false;
    }

    return $supported;
}

function analytics_filters_from_request(array $source): array
{
    $today = new DateTimeImmutable('today');
    $defaultStart = $today->modify('-29 days')->format('Y-m-d');
    $defaultEnd = $today->format('Y-m-d');

    $startDate = analytics_normalize_date((string) ($source['start_date'] ?? ''), $defaultStart);
    $endDate = analytics_normalize_date((string) ($source['end_date'] ?? ''), $defaultEnd);
    $selectedPath = trim((string) ($source['path'] ?? ''));
    $availablePaths = analytics_available_paths();

    if ($selectedPath !== '' && !in_array($selectedPath, $availablePaths, true)) {
        $selectedPath = '';
    }

    if ($startDate > $endDate) {
        [$startDate, $endDate] = [$endDate, $startDate];
    }

    return [
        'start_date' => $startDate,
        'end_date' => $endDate,
        'path' => $selectedPath,
        'available_paths' => $availablePaths,
    ];
}

function analytics_normalize_date(string $value, string $fallback): string
{
    $value = trim($value);

    if ($value === '') {
        return $fallback;
    }

    $date = DateTimeImmutable::createFromFormat('Y-m-d', $value);

    if (!$date || $date->format('Y-m-d') !== $value) {
        return $fallback;
    }

    return $value;
}

function analytics_filter_params(array $filters): array
{
    $start = new DateTimeImmutable((string) $filters['start_date'] . ' 00:00:00');
    $endExclusive = (new DateTimeImmutable((string) $filters['end_date'] . ' 00:00:00'))->modify('+1 day');

    return [
        'start_at' => $start->format('Y-m-d H:i:s'),
        'end_at' => $endExclusive->format('Y-m-d H:i:s'),
    ];
}

function analytics_filter_sql(array $filters, string $visitedColumn = 'visited_at', string $pathColumn = 'path'): array
{
    $params = analytics_filter_params($filters);
    $conditions = [
        $visitedColumn . ' >= :start_at',
        $visitedColumn . ' < :end_at',
    ];

    if (($filters['path'] ?? '') !== '') {
        $conditions[] = $pathColumn . ' = :path';
        $params['path'] = (string) $filters['path'];
    }

    return [
        'sql' => ' WHERE ' . implode(' AND ', $conditions),
        'params' => $params,
    ];
}

function analytics_filter_summary(array $filters): array
{
    $start = new DateTimeImmutable((string) $filters['start_date']);
    $end = new DateTimeImmutable((string) $filters['end_date']);
    $days = (int) $start->diff($end)->format('%a') + 1;

    return [
        'range_label' => $start->format('M j, Y') . ' to ' . $end->format('M j, Y'),
        'days' => $days,
        'path_label' => ($filters['path'] ?? '') !== '' ? (string) $filters['path'] : 'All public pages',
    ];
}

function analytics_available_paths(): array
{
    if (!analytics_tracking_is_supported()) {
        return [];
    }

    return array_map(
        static fn(array $row): string => (string) $row['path'],
        db_fetch_all(
            'SELECT DISTINCT path
             FROM site_visits
             ORDER BY path ASC'
        )
    );
}

function analytics_overview(array $filters): array
{
    if (!analytics_tracking_is_supported()) {
        return [
            'pageviews' => 0,
            'unique_visitors' => 0,
            'average_daily_pageviews' => 0,
            'peak_day_pageviews' => 0,
            'tracked_pages' => 0,
            'referrers' => 0,
        ];
    }

    $filter = analytics_filter_sql($filters);
    $days = max(1, (int) analytics_filter_summary($filters)['days']);
    $pageviews = (int) db_fetch_value('SELECT COUNT(*) FROM site_visits' . $filter['sql'], $filter['params'], 0);

    return [
        'pageviews' => $pageviews,
        'unique_visitors' => (int) db_fetch_value('SELECT COUNT(DISTINCT visitor_key) FROM site_visits' . $filter['sql'], $filter['params'], 0),
        'average_daily_pageviews' => (int) round($pageviews / $days),
        'peak_day_pageviews' => (int) db_fetch_value(
            'SELECT COALESCE(MAX(day_pageviews), 0)
             FROM (
                SELECT DATE(visited_at) AS visit_date, COUNT(*) AS day_pageviews
                FROM site_visits' . $filter['sql'] . '
                GROUP BY DATE(visited_at)
             ) daily_counts',
            $filter['params'],
            0
        ),
        'tracked_pages' => (int) db_fetch_value('SELECT COUNT(DISTINCT path) FROM site_visits' . $filter['sql'], $filter['params'], 0),
        'referrers' => (int) db_fetch_value('SELECT COUNT(DISTINCT referrer) FROM site_visits' . $filter['sql'] . ' AND referrer IS NOT NULL AND referrer <> ""', $filter['params'], 0),
    ];
}

function analytics_daily_visits(array $filters): array
{
    if (!analytics_tracking_is_supported()) {
        return [];
    }

    $filter = analytics_filter_sql($filters);
    $rows = db_fetch_all(
        'SELECT DATE(visited_at) AS visit_date,
                COUNT(*) AS pageviews,
                COUNT(DISTINCT visitor_key) AS visitors
         FROM site_visits' . $filter['sql'] . '
         GROUP BY DATE(visited_at)
         ORDER BY visit_date ASC',
        $filter['params']
    );

    $indexedRows = [];
    foreach ($rows as $row) {
        $indexedRows[(string) $row['visit_date']] = [
            'visit_date' => (string) $row['visit_date'],
            'pageviews' => (int) $row['pageviews'],
            'visitors' => (int) $row['visitors'],
        ];
    }

    $filledRows = [];
    $cursor = new DateTimeImmutable((string) $filters['start_date']);
    $end = new DateTimeImmutable((string) $filters['end_date']);

    while ($cursor <= $end) {
        $key = $cursor->format('Y-m-d');
        $filledRows[] = $indexedRows[$key] ?? [
            'visit_date' => $key,
            'pageviews' => 0,
            'visitors' => 0,
        ];
        $cursor = $cursor->modify('+1 day');
    }

    return $filledRows;
}

function analytics_daily_chart_points(array $dailyVisits): array
{
    if ($dailyVisits === []) {
        return [];
    }

    $maxPageviews = max(array_map(static fn(array $row): int => (int) $row['pageviews'], $dailyVisits));
    $maxPageviews = max(1, $maxPageviews);

    return array_map(
        static function (array $row) use ($maxPageviews): array {
            $height = max(8, (int) round(((int) $row['pageviews'] / $maxPageviews) * 100));

            return [
                'label' => (new DateTimeImmutable((string) $row['visit_date']))->format('M j'),
                'pageviews' => (int) $row['pageviews'],
                'visitors' => (int) $row['visitors'],
                'height' => $height,
            ];
        },
        $dailyVisits
    );
}

function analytics_top_pages(array $filters, int $limit = 10): array
{
    if (!analytics_tracking_is_supported()) {
        return [];
    }

    $limit = max(1, min($limit, 25));
    $filter = analytics_filter_sql($filters);
    $totalPageviews = (int) db_fetch_value('SELECT COUNT(*) FROM site_visits' . $filter['sql'], $filter['params'], 0);

    $rows = db_fetch_all(
        'SELECT path,
                COUNT(*) AS pageviews,
                COUNT(DISTINCT visitor_key) AS visitors,
                MAX(visited_at) AS last_visit_at
         FROM site_visits' . $filter['sql'] . '
         GROUP BY path
         ORDER BY pageviews DESC, visitors DESC, path ASC
         LIMIT ' . $limit,
        $filter['params']
    );

    $maxPageviews = 1;
    foreach ($rows as $row) {
        $maxPageviews = max($maxPageviews, (int) $row['pageviews']);
    }

    return array_map(
        static function (array $row) use ($totalPageviews, $maxPageviews): array {
            $pageviews = (int) $row['pageviews'];

            return $row + [
                'share_percent' => $totalPageviews > 0 ? round(($pageviews / $totalPageviews) * 100, 1) : 0,
                'bar_percent' => max(8, (int) round(($pageviews / $maxPageviews) * 100)),
            ];
        },
        $rows
    );
}

function analytics_recent_referrers(array $filters, int $limit = 10): array
{
    if (!analytics_tracking_is_supported()) {
        return [];
    }

    $limit = max(1, min($limit, 25));
    $filter = analytics_filter_sql($filters);

    return db_fetch_all(
        'SELECT referrer,
                COUNT(*) AS visits,
                MAX(visited_at) AS last_visit_at
         FROM site_visits' . $filter['sql'] . ' AND referrer IS NOT NULL AND referrer <> ""
         GROUP BY referrer
         ORDER BY visits DESC, last_visit_at DESC
         LIMIT ' . $limit,
        $filter['params']
    );
}
