<?php
if(!isset($_SESSION['userid'])){
    exit("Unauthorized Access");
}

ini_set('max_execution_time', 300);
date_default_timezone_set('Asia/Dhaka');

/*
|--------------------------------------------------------------------------
| CONFIG
|--------------------------------------------------------------------------
*/

$oltIp = "113.212.111.148";
$port = "2323";
$username = "admin";
$password = "admin";

/*
|--------------------------------------------------------------------------
| CACHE CONFIG
|--------------------------------------------------------------------------
*/

$cacheDir = __DIR__ . '/cache';

$cacheOnu = $cacheDir . '/onu_cache.txt';
$cacheAllMac = $cacheDir . '/mac_all.txt';
$cacheMikrotik = $cacheDir . '/mikrotik_cache.json';

$onuCacheTime = 500;
$macCacheTime = 500;
$mikrotikCacheTime = 500;

if (!file_exists($cacheDir)) {
    mkdir($cacheDir, 0777, true);
}

/*
|--------------------------------------------------------------------------
| CACHE HELPER
|--------------------------------------------------------------------------
*/

function getCacheData($cacheFile, $ttl, $callback){

    if(file_exists($cacheFile)){
        if((time() - filemtime($cacheFile)) < $ttl){
            $data = file_get_contents($cacheFile);
            return json_decode($data, true) ?: $data;
        }
    }

    $data = $callback();

    if(is_array($data)){
        file_put_contents($cacheFile, json_encode($data));
    }else{
        file_put_contents($cacheFile, $data);
    }

    return $data;
}

/*
|--------------------------------------------------------------------------
| HELPERS
|--------------------------------------------------------------------------
*/

function formatMac($mac){
    $mac = strtolower(trim($mac));
    $mac = str_replace(['.', '-', ':'], '', $mac);
    return implode(':', str_split($mac, 2));
}

/*
|--------------------------------------------------------------------------
| TELNET
|--------------------------------------------------------------------------
*/

function runTelnetCommand($command, $timeout = 10){

    global $oltIp, $port, $username, $password;

    $script = "/usr/bin/expect -c '
    set timeout $timeout
    spawn telnet $oltIp $port

    expect \"Username:\"
    send \"$username\r\"

    expect \"Password:\"
    send \"$password\r\"

    expect -re \".*>\" 

    send \"enable\r\"
    expect -re \".*#\"

    send \"terminal length 0\r\"
    expect -re \".*#\"

    send \"$command\r\"
    expect -re \".*#\"

    send \"exit\r\"
    expect eof
    '";

    return shell_exec($script);
}

/*
|--------------------------------------------------------------------------
| ONU PARSE
|--------------------------------------------------------------------------
*/

function parseOnuList($output){

    $data = [];

    $output = str_replace("\r", "", $output);

    foreach(explode("\n", $output) as $line){

        $line = trim($line);

        // skip header lines
        if(
            $line == '' ||
            stripos($line, 'IntfName') !== false ||
            stripos($line, 'MAC Address') !== false ||
            stripos($line, '----') !== false
        ){
            continue;
        }

        /*
        BDCOM format:
        EPON0/1:1 CDTC 11AF 505b.1db3.a8ce N/A static deregistered power-off
        */

        if(preg_match('/^(EPON\S+)\s+(\S+)\s+(\S+)\s+([0-9a-fA-F\.:-]+)\s+.*?(active|online|offline|off-line|deregistered|auto-configured)/i', $line, $m)){

            $rawMac = strtolower($m[4]);
            $rawMac = str_replace([':', '.', '-'], '', $rawMac);

            $data[] = [
                "interface" => strtolower($m[1]),
                "vendor"    => strtolower($m[2]),
                "model"     => $m[3],
                "mac"       => implode(':', str_split($rawMac, 2)),
                "status"    => (in_array(strtolower($m[5]), ['active','online','auto-configured'])) ? "UP" : "DOWN"
            ];
        }
    }

    return $data;
}

/*
|--------------------------------------------------------------------------
| MAC TABLE PARSE
|--------------------------------------------------------------------------
*/

function parseMacTable($output){

    $data = [];
    $capture = false;

    foreach(explode("\n", $output) as $line){

        $line = trim($line);

        // header detect (BDCOM flexible)
        if(preg_match('/vlan.*mac.*address/i', $line)){
            $capture = true;
            continue;
        }

        if(!$capture || $line === '' || preg_match('/^-+$/', $line)){
            continue;
        }

        /*
        |----------------------------------------------------
        | BDCOM supports multiple MAC formats:
        | aabb.ccdd.eeff
        | aa:bb:cc:dd:ee:ff
        | aabbccddeeff
        |----------------------------------------------------
        */

        if(preg_match('/^(\d+)\s+([0-9a-fA-F:.]{11,17})\s+(\w+)\s+(.+)$/', $line, $m)){

            $data[] = [
                'vlan' => (int)$m[1],
                'mac'  => formatMac($m[2]),
                'type' => strtoupper($m[3]),
                'port' => strtolower(trim($m[4]))
            ];
        }
    }

    return $data;
}

/*
|--------------------------------------------------------------------------
| GROUP MAC
|--------------------------------------------------------------------------
*/

function groupMacByInterface($macList){

    $grouped = [];

    foreach($macList as $m){

        $port = strtolower($m['port']);

        if(strpos($port,'epon') === false) continue;

        $base = explode('-', $port)[0];

        $grouped[$base][] = $m;
    }

    return $grouped;
}

/*
|--------------------------------------------------------------------------
| LOAD DATA
|--------------------------------------------------------------------------
*/

$onuOutput = getCacheData($cacheOnu, 1800, function(){
    return runTelnetCommand("show epon onu-information");
});

$macOutput = getCacheData($cacheAllMac, 1800, function(){
    return runTelnetCommand("show mac address-table", 15);
});

$mikrotikMap = getCacheData($cacheMikrotik, 900, function() use ($obj){

    $users = $obj->pppeoActiveSecretList(1);

    $map = [];

    foreach($users as $u){

        if(!empty($u['caller-id'])){

            $raw = strtolower($u['caller-id']);
            $raw = str_replace(['-','.'], '', $raw);

            $mac = formatMac($raw);

            $map[$mac] = [
                'name' => $u['name'],
                'router_mac' => $mac
            ];
        }
    }

    return $map;
});

$allOnu = parseOnuList($onuOutput);
$allMacList = parseMacTable($macOutput);
$macGrouped = groupMacByInterface($allMacList);

/*
|--------------------------------------------------------------------------
| PAGINATION
|--------------------------------------------------------------------------
*/
$perPage = isset($_GET['limit']) ? (int)$_GET['limit'] : 10;
if(!in_array($perPage, [10,20,50,100])){
    $perPage = 10;
}

$currentPage = isset($_GET['pg']) ? max(1,(int)$_GET['pg']) : 1;

$totalPage = ceil(count($allOnu)/$perPage);

$onus = array_slice($allOnu, ($currentPage-1)*$perPage, $perPage);

/*
|--------------------------------------------------------------------------
| UPDATE LIST (FOR DB)
|--------------------------------------------------------------------------
*/



?>

<!DOCTYPE html>
<html>
<head>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
</head>

<body class="bg-light">

<div class="container mt-4">

<h4>ONU Monitoring Dashboard</h4>

<form method="post">
    <button type="submit" name="sync_now" class="btn btn-primary mb-3">
        🔄 Sync Now Only This page
    </button>
</form>
<form method="get" class="mb-3 d-flex gap-2">
    <input type="hidden" name="page" value="onu_info">

    <select name="limit" class="form-select w-auto" onchange="this.form.submit()">
        <option value="10" <?= $perPage==10?'selected':'' ?>>10</option>
        <option value="20" <?= $perPage==20?'selected':'' ?>>20</option>
        <option value="50" <?= $perPage==50?'selected':'' ?>>50</option>
        <option value="100" <?= $perPage==100?'selected':'' ?>>100</option>
    </select>

    <noscript><button class="btn btn-primary">Apply</button></noscript>
</form>
<div class="alert alert-dark">
    <strong>Total ONU:</strong> <?= count($allOnu) ?> |
    <strong>Showing:</strong> <?= count($onus) ?>
</div>
<table class="table table-bordered">

<thead class="table-dark">
<tr>
<th>Interface</th>
<th>Vendor</th>
<th>Model</th>
<th>ONU MAC</th>
<th>Status</th>
<th>Sync</th>
<th>User</th>
<th>Router MAC</th>
</tr>
</thead>

<tbody>

<?php foreach($onus as $o):

    $matchedUser = null;
    $routerMac = null;

    $macList = $macGrouped[strtolower($o['interface'])] ?? [];

    foreach($macList as $m){

        if($m['type'] != 'DYNAMIC') continue;

        $oltMac = str_replace(':','',strtolower($m['mac']));

        foreach($mikrotikMap as $mk => $user){

            $mkClean = str_replace(':','',strtolower($mk));

            if($oltMac == $mkClean){

                $matchedUser = $user['name'];
                $routerMac = $user['router_mac'];
                break 2;
            }
        }
    }

    /*
    |--------------------------------------------------------------------------
    | STORE FOR BULK UPDATE
    |--------------------------------------------------------------------------
    */

    if($matchedUser){
        $updateList[$matchedUser] = $o['mac'];
    }

?>

<tr>
<td><?= $o['interface'] ?></td>
<td><?= $o['vendor'] ?></td>
<td><?= $o['model'] ?></td>
<td><?= $o['mac'] ?></td>

<td>
<?= ($o['status']=="UP")
    ? "<span class='badge bg-success'>UP</span>"
    : "<span class='badge bg-danger'>DOWN</span>" ?>
</td>

<td>
<?= $matchedUser ? "<span class='badge bg-success'>Matched</span>" : "<span class='badge bg-warning text-dark'>No Client</span>" ?>
</td>

<td><?= $matchedUser ?: 'N/A' ?></td>

<td><?= $routerMac ?: 'N/A' ?></td>

</tr>

<?php endforeach; ?>

</tbody>
</table>

<?php if($totalPage > 1): ?>
<nav>
<ul class="pagination">

<?php for($i=1;$i<=$totalPage;$i++): ?>
<li class="page-item <?= ($i==$currentPage)?'active':'' ?>">
<a class="page-link"
href="?page=onu_info&pg=<?= $i ?>&limit=<?= $perPage ?>">
<?= $i ?>
</a>
</li>
<?php endfor; ?>

</ul>
</nav>
<?php endif; ?>

</div>

</body>
</html>

<?php

/*
|--------------------------------------------------------------------------
| DATABASE UPDATE (tbl_agent onu_mac)
|--------------------------------------------------------------------------
*/

if(isset($_POST['sync_now'])){

    $updateList = [];

    foreach($allOnu as $o){

        $matchedUser = null;

        $macList = $macGrouped[strtolower($o['interface'])] ?? [];

        foreach($macList as $m){

            if($m['type'] != 'DYNAMIC') continue;

            $oltMac = strtolower(str_replace([':','.','-'], '', $m['mac']));

            foreach($mikrotikMap as $mk => $user){

                $mkClean = strtolower(str_replace([':','.','-',' '], '', $mk));

                if($oltMac == $mkClean){

                    $matchedUser = trim($user['name']); // secret name
                    break 2;
                }
            }
        }

        if($matchedUser){

            $updateList[$matchedUser][] = $o['mac'];
        }
    }

    foreach($updateList as $user => $macs){

        $macString = implode(',', array_unique($macs));

        // 🔥 SAFE UPDATE (force check)
        $result = $obj->updateData(
            'tbl_agent',
            ['onumac' => $macString],
            ['TRIM(ip)' => trim($user)]
        );

        // optional debug
        if(!$result){
            error_log("FAILED: ".$user);
        }
    }

    echo "<script>alert('Sync Completed');</script>";
}
?>