<?php

function findLoader(): string {
    $dir = __DIR__;
    for ($i = 0; $i < 6; $i++) {
        $candidate = $dir . '/lib/php/loader.php';
        if (file_exists($candidate)) {
            return $candidate;
        }
        $dir = dirname($dir);
    }
    die("Could not locate loader.php — ensure this tool is installed within the WMD SED directory tree.\n");
}
require findLoader();

use CodePunch\Base\Util as UTIL;

// =============================================================================
// TOOL CONSTANTS
// =============================================================================

define('THIS_TOOL_NAME', 'wcag');
define('WCAG_CONFIG_KEY', str_replace('-', '_', THIS_TOOL_NAME) . '_tool');

// =============================================================================
// WCAG CONSTANTS — must match wcag-gather.php
// =============================================================================

const WCAG_OUTPUT_FOLDER    = 'wcag-data';
const WCAG_DEFAULT_CATEGORY = 'WCAG Test Domains';
const COL_STATUS            = 'wcag_status';
const COL_LEVEL             = 'wcag_level';
const COL_ISSUE_COUNTS      = 'wcag_issue_counts';
const COL_SCANNED_AT        = 'wcag_scanned_at';

require_once "includes/base-init.php";

// =============================================================================
// CLI GUARD
// =============================================================================

if (UTIL::is_cli()) {
    UTIL::print("This tool must be run in a browser.");
    exit;
}

// =============================================================================
// AUTH + SESSION
// =============================================================================

$auth = new \CodePunch\Config\Auth(\CodePunch\DB\Database::STANDARD_INIT);
if (!$auth) {
    header("Location: ../index.php?logout&return=tools/wcag");
    exit;
}
$hasProClass = $auth->getProClass(THIS_TOOL_NAME);
$requiresPro = $auth->hasProClass(THIS_TOOL_NAME, true);
if(!$hasProClass && $requiresPro) {
	header("Location: ../tools/");
    exit;
}

$sessionStatus = $auth->validateSession(false, false);

// Detect AJAX requests — these must get JSON errors, not redirects
$isAjax = isset($_SERVER['HTTP_X_REQUESTED_WITH'])
    || (isset($_GET['action']) && $_GET['action'] !== '');

if ($sessionStatus !== \CodePunch\Config\Auth::VALID) {
    if ($isAjax) {
        header('Content-Type: application/json');
        echo json_encode([
            'error'   => 'session_expired',
            'message' => 'Your session has expired. Please log in again.'
        ]);
        exit;
    }

    header("Location: ../index.php?logout&return=tools/wcag");
    exit;
}

if (!\CodePunch\Config\Auth::isAdmin()) {
    if ($isAjax) {
        header('Content-Type: application/json');
        echo json_encode([
            'error'   => 'auth_required',
            'message' => 'Administrator access required.'
        ]);
        exit;
    }

    UTIL::print("Access denied — administrator privileges required.");
    exit;
}

$db = $auth->getDatabase();
if (!$db) {
    UTIL::print("Database connection failed.");
    exit;
}

// =============================================================================
// REQUEST STATE
// =============================================================================

$rd = UTIL::get_unsafe_request_data_array();
$table = $db->getDomainTableName();

// =============================================================================
// PATHS + URLS
// =============================================================================

$logBase   = UTIL::get_log_folder_path();
$outFolder = $logBase . WCAG_OUTPUT_FOLDER . DIRECTORY_SEPARATOR;

// Build the WMD root URL regardless of installation path or folder depth.
// UTIL::get_install_folder_path() returns the absolute disk path to the WMD root.
// We find how many path segments separate the current script from the WMD root,
// then walk up SCRIPT_NAME by the same number of steps.
$wmdDiskRoot = rtrim(UTIL::get_install_folder_path(), DIRECTORY_SEPARATOR);
$scriptDisk  = rtrim(dirname($_SERVER['SCRIPT_FILENAME']), DIRECTORY_SEPARATOR);
$depth       = 0;
$dir         = $scriptDisk;

while ($dir !== $wmdDiskRoot && $dir !== dirname($dir)) {
    $depth++;
    $dir = dirname($dir);
}

$protocol   = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https://' : 'http://';
$scriptUrl  = dirname($_SERVER['SCRIPT_NAME']);
$wmdUrlRoot = $scriptUrl;

for ($i = 0; $i < $depth; $i++) {
    $wmdUrlRoot = dirname($wmdUrlRoot);
}

// =============================================================================
// ACTION HANDLERS
// =============================================================================

$action = isset($rd['action']) ? trim($rd['action']) : '';

if ($action === 'get_violations') {
    header('Content-Type: application/json');

    $domain = isset($rd['domain']) ? basename(trim($rd['domain'])) : '';
    if ($domain === '') {
        echo json_encode(['error' => 'No domain specified.']);
        exit;
    }

    $jsonFile = $outFolder . $domain . '.json';
    if (!file_exists($jsonFile)) {
        echo json_encode(['error' => 'No scan file found for: ' . $domain]);
        exit;
    }

    $raw = file_get_contents($jsonFile);
    if ($raw === false) {
        echo json_encode(['error' => 'Could not read scan file.']);
        exit;
    }

    echo $raw;
    exit;
}

if ($action === 'export_json') {
    header('Content-Type: application/json');
    header('Content-Disposition: attachment; filename="wcag-export-' . date('Y-m-d') . '.json"');

    $showAllExport = isset($rd['all']) && $rd['all'] === '1';
    $exportRows = fetchAllRows($db, $table, $showAllExport ? [] : resolveCategorySids($db));
    $export = [];

    foreach ($exportRows as $row) {
        $entry = $row;
        $entry['issueCounts'] = json_decode($row[COL_ISSUE_COUNTS] ?? '{}', true);

        $jsonFile = $outFolder . $row['domain'] . '.json';
        if (file_exists($jsonFile)) {
            $full = json_decode(file_get_contents($jsonFile), true);
            $entry['violations'] = $full['violations'] ?? [];
            $entry['incomplete'] = $full['incomplete'] ?? [];
            $entry['passes'] = $full['passes'] ?? 0;
        }

        $export[] = $entry;
    }

    echo json_encode($export, JSON_PRETTY_PRINT);
    exit;
}

if ($action === 'export_csv') {
    header('Content-Type: text/csv');
    header('Content-Disposition: attachment; filename="wcag-export-' . date('Y-m-d') . '.csv"');

    $showAllExport = isset($rd['all']) && $rd['all'] === '1';
    $exportRows = fetchAllRows($db, $table, $showAllExport ? [] : resolveCategorySids($db));

    $out = fopen('php://output', 'w');
    fputcsv($out, ['Domain', 'Status', 'WCAG Level', 'Critical', 'Serious', 'Moderate', 'Minor', 'Scanned At']);

    foreach ($exportRows as $row) {
        $counts = json_decode($row[COL_ISSUE_COUNTS] ?? '{}', true) ?: [];
        fputcsv($out, [
            $row['domain'],
            $row[COL_STATUS] ?? '',
            $row[COL_LEVEL] ?? '',
            $counts['critical'] ?? 0,
            $counts['serious'] ?? 0,
            $counts['moderate'] ?? 0,
            $counts['minor'] ?? 0,
            $row[COL_SCANNED_AT] ?? '',
        ]);
    }

    fclose($out);
    exit;
}

if ($action === 'reset_scan') {
    header('Content-Type: application/json');

    $sid = isset($rd['sid']) ? (int)$rd['sid'] : 0;
    if ($sid <= 0) {
        echo json_encode(['error' => 'Invalid domain ID.']);
        exit;
    }

    $result = $db->updateTable($table, [COL_SCANNED_AT => null], "sid=?", [$sid]);

    if ($result === false) {
        echo json_encode(['error' => 'Database update failed.']);
    } else {
        echo json_encode(['ok' => true, 'sid' => $sid]);
    }

    exit;
}

// =============================================================================
// MAIN PAGE DATA
// =============================================================================

// Default: show only domains in the WCAG category. ?all=1 shows everything.
$showAll      = isset($rd['all']) && $rd['all'] === '1';
$categorySids = $showAll ? [] : resolveCategorySids($db);
$rows         = fetchAllRows($db, $table, $categorySids);
$stats        = buildStats($rows);


?><!DOCTYPE HTML>
<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <base href="<?php echo $baseUrl; ?>">
    <link rel="shortcut icon" type="image/x-icon" href="lib/layouts/images/favicon.ico">
    <meta name="author" content="">
    <title>Watch My Domains Server Edition v6</title>
    <meta name="description" content="">
    <meta name="keywords" content="">
    <meta name="dcterms.rightsHolder" content="Softnik Technologies">

    <link rel="preload" as="font" href="lib/layouts/fonts/Quicksand/quicksand-v28-latin-regular.woff2" type="font/woff2" crossorigin="anonymous">
    <link rel="preload" as="font" href="lib/layouts/fonts/Roboto-Slab/roboto-slab-v22-latin-regular.woff2" type="font/woff2" crossorigin="anonymous">
    <link rel="preload" as="font" href="lib/layouts/fonts/Josefin-Sans/josefin-sans-v23-latin-regular.woff2" type="font/woff2" crossorigin="anonymous">
    <link rel="preload" as="font" href="lib/layouts/fonts/Quicksand/quicksand-v28-latin-700.woff2" type="font/woff2" crossorigin="anonymous">
    <link rel="preload" as="font" href="lib/layouts/fonts/Roboto-Slab/roboto-slab-v22-latin-700.woff2" type="font/woff2" crossorigin="anonymous">
    <link rel="preload" as="font" href="lib/layouts/fonts/Josefin-Sans/josefin-sans-v23-latin-600.woff2" type="font/woff2" crossorigin="anonymous">
    <link rel="preload" as="font" href="lib/layouts/fonts/Open-Sans/open-sans-v40-greek_hebrew_latin_vietnamese-regular.woff2" type="font/woff2" crossorigin="anonymous">
    <link rel="preload" as="font" href="lib/layouts/fonts/Open-Sans/open-sans-v40-greek_hebrew_latin_vietnamese-700.woff2" type="font/woff2" crossorigin="anonymous">

    <link href="lib/layouts/fonts/Quicksand/fonts.css" rel="stylesheet">
    <link href="lib/layouts/fonts/Roboto-Slab/fonts.css" rel="stylesheet">
    <link href="lib/layouts/fonts/Josefin-Sans/fonts.css" rel="stylesheet">
    <link href="lib/layouts/fonts/Open-Sans/fonts.css" rel="stylesheet">
    <link href="lib/layouts/css/min/common.min.css" rel="stylesheet">
    <link href="lib/layouts/modules/common/css/min/common.min.css" rel="stylesheet">

    <script src="api.php?c=js&file=data.js&loc=modules/toolbox" type="text/javascript"></script>

    <link rel="stylesheet" type="text/css" href="lib/layouts/colors/light/light.css" id="theme-mode-css">
    <link rel="stylesheet" type="text/css" href="lib/layouts/colors/light/themes/Default/default.css" id="theme-default-css">
    <link rel="stylesheet" type="text/css" href="lib/layouts/colors/light/themes/Default/styles.css" id="theme-styles-css">
    <link rel="stylesheet" type="text/css" href="lib/php/styles.php" id="theme-bg-css">

    <link href="tools/css/common.css" rel="stylesheet">
    <link href="tools/css/common-dark.css" rel="stylesheet">
    <link href="tools/css/toggle.css" rel="stylesheet">
    <link href="tools/css/wcag.css" rel="stylesheet">
</head>
<body>

<div class="theme-select theme-bg" id="themechange"></div>
<div class="menu-select theme-bg" id="menuselect"></div>

<div id="contentwrap">
	<?php include "includes/head-logo-toolbar.php"; ?>

    <div id="content">

        <div id="maincontent">
            <div id="domaindata">
                <div class="wcag-panel">

                    <div class="panel-header">
                        <h1>WCAG Accessibility Report</h1>
                    </div>

                    <div class="wrap">

                        <div class="stats">
                            <div class="sc"><div class="n"><?= $stats['total'] ?></div><div class="l">Total</div></div>
                            <div class="sc"><div class="n"><?= $stats['scanned'] ?></div><div class="l">Scanned</div></div>
                            <div class="sc"><div class="n cn-pass"><?= $stats['pass'] ?></div><div class="l">Pass</div></div>
                            <div class="sc"><div class="n cn-fail"><?= $stats['fail'] ?></div><div class="l">Fail</div></div>
                            <div class="sc"><div class="n cn-partial"><?= $stats['partial'] ?></div><div class="l">Partial</div></div>
                            <div class="sc"><div class="n cn-grey"><?= $stats['error'] ?></div><div class="l">Error</div></div>
                            <div class="sc"><div class="n cn-grey"><?= $stats['not_scanned'] ?></div><div class="l">Not Scanned</div></div>
                        </div>

						<div class="toolbar">
							<input type="text" id="srch" placeholder="Filter domains…" oninput="applyFilters()">

							<select id="fScope" onchange="changeScope(this.value)">
								<option value="category" <?= $showAll ? '' : 'selected' ?>>WCAG Category Only</option>
								<option value="all" <?= $showAll ? 'selected' : '' ?>>Show All Domains</option>
							</select>

							<select id="fSt" onchange="applyFilters()">
								<option value="">All statuses</option>
								<option value="pass">Pass</option>
								<option value="fail">Fail</option>
								<option value="partial">Partial</option>
								<option value="error">Error</option>
							</select>

							<select id="fLv" onchange="applyFilters()">
								<option value="">All levels</option>
								<option value="AAA">AAA</option>
								<option value="AA">AA</option>
								<option value="A">A</option>
								<option value="none">None</option>
							</select>

							<span class="rc" id="rc"></span>
							<button class="btn" onclick="doExport('csv')">&#8595; CSV</button>
							<button class="btn" onclick="doExport('json')">&#8595; JSON</button>
						</div>

                        <div class="tbl-wrap">
                            <table>
                                <thead>
                                    <tr>
                                        <th onclick="sortBy(0,'domain')">Domain<span class="sa" id="sa0"></span></th>
                                        <th onclick="sortBy(1,'status')">Status<span class="sa" id="sa1"></span></th>
                                        <th onclick="sortBy(2,'level')">Level<span class="sa" id="sa2"></span></th>
                                        <th onclick="sortBy(3,'crit')">Critical<span class="sa" id="sa3"></span></th>
                                        <th onclick="sortBy(4,'serious')">Serious<span class="sa" id="sa4"></span></th>
                                        <th onclick="sortBy(5,'mod')">Moderate<span class="sa" id="sa5"></span></th>
                                        <th onclick="sortBy(6,'minor')">Minor<span class="sa" id="sa6"></span></th>
                                        <th onclick="sortBy(7,'scanned')">Scanned At<span class="sa" id="sa7"></span></th>
                                        <th style="width:36px"></th>
                                    </tr>
                                </thead>
                                <tbody id="tb">
                                <?php if (empty($rows)): ?>
                                    <tr class="empty"><td colspan="9">No scan data found. Run wcag-gather.php to begin.</td></tr>
                                <?php else: ?>
                                <?php foreach ($rows as $rawRow): ?>
                                    <?php $row = prepareRow($rawRow); ?>
                                    <tr onclick="openPopup(<?= htmlspecialchars(json_encode($row['domain']), ENT_QUOTES) ?>)"
                                        data-domain="<?= htmlspecialchars($row['domain']) ?>"
                                        data-status="<?= htmlspecialchars($row['status']) ?>"
                                        data-level="<?= htmlspecialchars($row['level']) ?>"
                                        data-sid="<?= $row['sid'] ?>"
                                        data-crit="<?= $row['crit'] ?>"
                                        data-serious="<?= $row['serious'] ?>"
                                        data-mod="<?= $row['mod'] ?>"
                                        data-minor="<?= $row['minor'] ?>"
                                        data-scanned="<?= htmlspecialchars($row['scannedAt']) ?>">
                                        <td><?= htmlspecialchars($row['domain']) ?></td>
                                        <td><?= $row['status'] ? '<span class="badge ' . $row['statusBadgeClass'] . '">' . htmlspecialchars($row['status']) . '</span>' : '—' ?></td>
                                        <td><?= $row['level'] ? '<span class="badge ' . $row['levelBadgeClass'] . '">' . htmlspecialchars($row['level']) . '</span>' : '—' ?></td>
                                        <td class="sc-c"><?= $row['crit'] ?: '—' ?></td>
                                        <td class="sc-s"><?= $row['serious'] ?: '—' ?></td>
                                        <td class="sc-m"><?= $row['mod'] ?: '—' ?></td>
                                        <td class="sc-n"><?= $row['minor'] ?: '—' ?></td>
                                        <td><?= htmlspecialchars($row['scannedAt']) ?: '—' ?></td>
                                        <td class="reset-col">
                                            <button
                                                class="btn-reset"
                                                title="Reset scan date — will be re-scanned on next run"
                                                onclick="event.stopPropagation();resetScan(this,<?= $row['sid'] ?>)"
                                            >&#x21BA;</button>
                                        </td>
                                    </tr>
                                <?php endforeach; ?>
                                <?php endif; ?>
                                </tbody>
                            </table>
                        </div>

                    </div><!-- /wrap -->

                </div>
            </div>
        </div>

    </div>

</div>

<div class="overlay" id="ov" onclick="closePopup(event)">
    <div class="popup" onclick="event.stopPropagation()">
        <div class="pop-hd">
            <h2 id="popTitle">Loading…</h2>
            <button class="pop-close" onclick="closePopup()" title="Close (Esc)">&#x2715;</button>
        </div>
        <div class="pop-body" id="popBody">
            <p class="loading">Loading…</p>
        </div>
    </div>
</div>

<script>
var baseurl           = "<?php echo $baseUrl; ?>";
var API_BASE_URL      = <?php echo json_encode(htmlspecialchars($apiBaseUrl, ENT_QUOTES, 'UTF-8')); ?>;
var IS_ADMIN          = <?php echo $isAdmin ? 'true' : 'false'; ?>;
var CURRENT_USER      = <?php echo json_encode($currentUserName); ?>;
var WCAG_CONFIG_KEY   = "<?php echo WCAG_CONFIG_KEY; ?>";
var CONFIG_CREATED_AT = <?php echo time(); ?>;
</script>

<script type="text/javascript" src="tools/js/common.js"></script>
<script type="text/javascript" src="tools/js/wcag.js"></script>

</body>
</html>

<?php

// =============================================================================
// FUNCTIONS
// =============================================================================

function resolveCategorySids($db): array {
    $cid = $db->findOneOf($db->getCategoryTableName(), "name", WCAG_DEFAULT_CATEGORY, "cid");
    if (!$cid) {
        return [];
    }

    $connRows = $db->getFromTable(
        "did",
        $db->getCategoryConnectionTableName(),
        "cid = ?",
        [(int)$cid],
        '',
        '',
        100000
    );

    if ($connRows === false || empty($connRows)) {
        return [];
    }
	$connRows = array_map(fn($row) => array_change_key_case($row, CASE_LOWER), $connRows);
    return array_map(fn($r) => (int)$r['did'], $connRows);
}

function fetchAllRows($db, string $table, array $sids = []): array {
    $columns = implode(',', ['sid', 'domain', COL_STATUS, COL_LEVEL, COL_ISSUE_COUNTS, COL_SCANNED_AT]);

    if (!empty($sids)) {
        $placeholders = implode(',', array_fill(0, count($sids), '?'));
        $rows = $db->getFromTable($columns, $table, "sid IN ($placeholders)", $sids, 'domain', 'asc', 100000);
    } else {
        $rows = $db->getFromTable($columns, $table, '1=1', [], 'domain', 'asc', 100000);
    }

    if ($rows === false) {
        return [];
    }
	$rows = array_map(fn($row) => array_change_key_case($row, CASE_LOWER), $rows);
    return $rows;
}

function buildStats(array $rows): array {
    $stats = [
        'total'       => count($rows),
        'scanned'     => 0,
        'pass'        => 0,
        'fail'        => 0,
        'partial'     => 0,
        'error'       => 0,
        'not_scanned' => 0,
    ];

    foreach ($rows as $row) {
        if (!empty($row[COL_SCANNED_AT])) {
            $stats['scanned']++;
        }

        $status = $row[COL_STATUS] ?? '';
        if ($status === 'pass') {
            $stats['pass']++;
        } elseif ($status === 'fail') {
            $stats['fail']++;
        } elseif ($status === 'partial') {
            $stats['partial']++;
        } elseif ($status === 'error') {
            $stats['error']++;
        }
    }

    $stats['not_scanned'] = $stats['total'] - $stats['scanned'];

    return $stats;
}

function prepareRow(array $row): array {
    $status = $row[COL_STATUS] ?? '';
    $level  = $row[COL_LEVEL] ?? '';
    $counts = json_decode($row[COL_ISSUE_COUNTS] ?? '{}', true) ?: [];

    return [
        'sid'              => (int)($row['sid'] ?? 0),
        'domain'           => $row['domain'] ?? '',
        'status'           => $status,
        'level'            => $level,
        'scannedAt'        => $row[COL_SCANNED_AT] ?? '',
        'crit'             => (int)($counts['critical'] ?? 0),
        'serious'          => (int)($counts['serious'] ?? 0),
        'mod'              => (int)($counts['moderate'] ?? 0),
        'minor'            => (int)($counts['minor'] ?? 0),
        'statusBadgeClass' => match ($status) {
            'pass'    => 'bp',
            'fail'    => 'bf',
            'partial' => 'bw',
            default   => 'be',
        },
        'levelBadgeClass'  => ($level && $level !== 'none') ? 'bl' : 'bn',
    ];
}

function isShowAllRequested(array $requestData): bool {
    return isset($requestData['all']) && (string)$requestData['all'] === '1';
}
