<?php
// admin/comms_admin_lib.php
// أدوات الاتصالات والإدارة: راوترات/قطاعات/Wizard + منع تكرار التنفيذ (Idempotency)

require_once __DIR__ . '/helpers.php';
require_once __DIR__ . '/routeros_api.php';
require_once __DIR__ . '/mikrotik_connection_lib.php';
require_once __DIR__ . '/db_bootstrap.php';
require_once __DIR__ . '/mikrotik_lib.php';

function comms_db(): mysqli {
  ensure_db(true);
  return $GLOBALS['conn'];
}

function comms_ensure_tables(mysqli $conn): void {
  // routers
  $conn->query("CREATE TABLE IF NOT EXISTS routers (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    host VARCHAR(255) NOT NULL,
    api_port INT NOT NULL DEFAULT 8728,
    username VARCHAR(100) NOT NULL,
    password VARCHAR(255) NOT NULL,
    model VARCHAR(120) NOT NULL DEFAULT '',
    role ENUM('uplink','pppoe','monitor','other') NOT NULL DEFAULT 'other',
    is_enabled TINYINT(1) NOT NULL DEFAULT 1,
    notes TEXT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");

  // توسعة جدول الراوترات لإضافة model (بدون تخريب)
  $chkR = $conn->query("SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='routers' AND COLUMN_NAME='model' LIMIT 1");
  if(!$chkR || $chkR->num_rows===0){
    @ $conn->query("ALTER TABLE routers ADD COLUMN model VARCHAR(120) NOT NULL DEFAULT '' AFTER password");
  }

  // إعدادات الاتصالات (scenario وغيره)
  $conn->query("CREATE TABLE IF NOT EXISTS comms_settings (
    k VARCHAR(120) PRIMARY KEY,
    v LONGTEXT NULL,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");

  // sectors (VLAN)
  $conn->query("CREATE TABLE IF NOT EXISTS sectors (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(120) NOT NULL,
    vlan_id INT NOT NULL,
    router_id INT NOT NULL,
    trunk_interface VARCHAR(64) NOT NULL DEFAULT 'ether2',
    bridge_name VARCHAR(64) NOT NULL DEFAULT 'bridge-pppoe',
    is_enabled TINYINT(1) NOT NULL DEFAULT 1,
    notes TEXT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    KEY (router_id)
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");

  // توسعة جدول القطاعات لإضافة Pools/PPPoE لكل VLAN (بدون تخريب)
  // نضيف الأعمدة فقط إذا كانت غير موجودة.
  $needCols = [
    'pppoe_pool_name'   => "ALTER TABLE sectors ADD COLUMN pppoe_pool_name VARCHAR(64) NOT NULL DEFAULT ''",
    'pppoe_pool_range'  => "ALTER TABLE sectors ADD COLUMN pppoe_pool_range VARCHAR(128) NOT NULL DEFAULT ''",
    'pppoe_local_ip'    => "ALTER TABLE sectors ADD COLUMN pppoe_local_ip VARCHAR(64) NOT NULL DEFAULT ''",
    'pppoe_service'     => "ALTER TABLE sectors ADD COLUMN pppoe_service VARCHAR(64) NOT NULL DEFAULT ''",
    'pppoe_profile'     => "ALTER TABLE sectors ADD COLUMN pppoe_profile VARCHAR(64) NOT NULL DEFAULT ''",
    'pppoe_server_name' => "ALTER TABLE sectors ADD COLUMN pppoe_server_name VARCHAR(64) NOT NULL DEFAULT ''"
  ];
  foreach($needCols as $col=>$sql){
    $colE = $conn->real_escape_string($col);
    $chk = $conn->query("SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='sectors' AND COLUMN_NAME='$colE' LIMIT 1");
    if(!$chk || $chk->num_rows===0){
      @$conn->query($sql);
    }
  }

  // action_log لمنع التكرار
  $conn->query("CREATE TABLE IF NOT EXISTS action_log (
    id INT AUTO_INCREMENT PRIMARY KEY,
    action_key VARCHAR(190) NOT NULL,
    payload_hash CHAR(64) NOT NULL,
    created_by INT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    UNIQUE KEY uniq_action (action_key, payload_hash)
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");

  $needActionCols = [
    'action'  => "ALTER TABLE action_log ADD COLUMN action VARCHAR(120) NULL AFTER created_by",
    'details' => "ALTER TABLE action_log ADD COLUMN details TEXT NULL AFTER action",
  ];
  foreach ($needActionCols as $col => $sql) {
    $colE = $conn->real_escape_string($col);
    $chk = $conn->query("SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='action_log' AND COLUMN_NAME='$colE' LIMIT 1");
    if (!$chk || $chk->num_rows === 0) {
      @ $conn->query($sql);
    }
  }
}

function comms_migrate(mysqli $conn): void {
  comms_ensure_tables($conn);
}

function comms_payload_hash(array $payload): string {
  return hash('sha256', json_encode($payload, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES));
}

function comms_action_once(mysqli $conn, string $action_key, array $payload, ?int $user_id, callable $fn): array {
  $hash = comms_payload_hash($payload);
  $key  = $conn->real_escape_string($action_key);
  $h    = $conn->real_escape_string($hash);
  $uid  = $user_id ? intval($user_id) : 'NULL';

  // هل نُفّذ سابقاً؟
  $q = $conn->query("SELECT id FROM action_log WHERE action_key='$key' AND payload_hash='$h' LIMIT 1");
  if ($q && $q->num_rows > 0) {
    return ['ok'=>true,'skipped'=>true,'msg'=>'تم تخطي التنفيذ لأنه منفّذ مسبقاً بنفس الإعدادات.'];
  }

  // نفّذ
  $res = $fn();

  // سجّل التنفيذ فقط إذا نجح
  if (is_array($res) && !empty($res['ok'])) {
    $conn->query("INSERT INTO action_log(action_key,payload_hash,created_by) VALUES('$key','$h',$uid)");
  }
  return $res;
}

function comms_get_routers(mysqli $conn): array {
  $rows = [];
  $r = $conn->query("SELECT * FROM routers ORDER BY id ASC");
  if ($r) while($row=$r->fetch_assoc()) $rows[]=$row;
  return $rows;
}

function comms_get_router(mysqli $conn, int $id): ?array {
  $id=intval($id);
  $r=$conn->query("SELECT * FROM routers WHERE id=$id LIMIT 1");
  if($r && $r->num_rows){ return $r->fetch_assoc(); }
  return null;
}


if (!function_exists('comms_router_get')) {
  function comms_router_get(mysqli $conn, int $id): ?array {
    return comms_get_router($conn, $id);
  }
}

if (!function_exists('comms_router_list')) {
  function comms_router_list(mysqli $conn): array {
    return comms_get_routers($conn);
  }
}

if (!function_exists('comms_sectors_list')) {
  function comms_sectors_list(mysqli $conn): array {
    return comms_get_sectors($conn);
  }
}

function comms_router_by_role(mysqli $conn, string $role): ?array {
  $role=$conn->real_escape_string($role);
  $r=$conn->query("SELECT * FROM routers WHERE role='$role' AND is_enabled=1 ORDER BY id ASC LIMIT 1");
  if($r && $r->num_rows){ return $r->fetch_assoc(); }
  return null;
}

function comms_mt_connect(array $router, string &$error): ?RouterOSAPI {
  $error = '';
  $api = ua_mt_connect_config($router, $error, 0.8);
  return ($api instanceof RouterOSAPI) ? $api : null;
}


function comms_mt_safe_disconnect(?RouterOSAPI $api): void {
  ua_mt_safe_disconnect($api);
}

// ===== تشخيص WAN (على Router1 غالباً) =====
function comms_detect_wan(mysqli $conn, array $router, string $wan_interface, string &$error): array {
  $error = '';
  $api = comms_mt_connect($router, $error);
  if(!$api) return ['ok'=>false,'error'=>$error];

  $info = [
    'wan_interface' => $wan_interface,
    'has_default_route' => false,
    'pppoe_client_running' => false,
    'dhcp_bound' => false,
    'static_ip' => null,
    'gateway' => null,
    'mode_detected' => 'unknown',
  ];

  try {
    // default route
    $routes = $api->comm('/ip/route/print', ['?dst-address'=>'0.0.0.0/0']);
    if (is_array($routes) && count($routes) > 0) {
      $info['has_default_route'] = true;
      $info['gateway'] = $routes[0]['gateway'] ?? null;
    }

    // pppoe-client on wan
    $pppoe = $api->comm('/interface/pppoe-client/print', ['?interface'=>$wan_interface]);
    if(is_array($pppoe) && count($pppoe)>0){
      // if any running
      foreach($pppoe as $c){
        if(($c['running'] ?? 'false') === 'true'){
          $info['pppoe_client_running'] = true;
          break;
        }
      }
    }

    // dhcp-client on wan
    $dhcp = $api->comm('/ip/dhcp-client/print', ['?interface'=>$wan_interface]);
    if(is_array($dhcp) && count($dhcp)>0){
      foreach($dhcp as $d){
        $st = $d['status'] ?? '';
        if(stripos($st,'bound')!==false){
          $info['dhcp_bound'] = true;
          break;
        }
      }
    }

    // static address on wan (ip/address bound to interface)
    $addr = $api->comm('/ip/address/print', ['?interface'=>$wan_interface]);
    if(is_array($addr) && count($addr)>0){
      // choose first global-like address
      $info['static_ip'] = $addr[0]['address'] ?? null;
    }

    // decide mode
    if($info['pppoe_client_running']) $info['mode_detected'] = 'pppoe';
    else if($info['dhcp_bound']) $info['mode_detected'] = 'dhcp';
    else if($info['static_ip'] && $info['has_default_route']) $info['mode_detected'] = 'static';
    else if($info['static_ip']) $info['mode_detected'] = 'static'; // partial
    else $info['mode_detected'] = 'unknown';

    comms_mt_safe_disconnect($api);
    return ['ok'=>true,'data'=>$info];
  } catch(Exception $e){
    comms_mt_safe_disconnect($api);
    return ['ok'=>false,'error'=>'خطأ أثناء التشخيص: '.$e->getMessage()];
  }
}

function comms_ping_test(mysqli $conn, array $router, string $target, string &$error): array {
  $error='';
  $api=comms_mt_connect($router,$error);
  if(!$api) return ['ok'=>false,'error'=>$error];
  try{
    $res=$api->comm('/ping', ['address'=>$target,'count'=>'3']);
    // RouterOSAPI قد يرجع array من النتائج أو assoc
    $ok = is_array($res);
    comms_mt_safe_disconnect($api);
    return ['ok'=>$ok,'data'=>$res];
  }catch(Exception $e){
    comms_mt_safe_disconnect($api);
    return ['ok'=>false,'error'=>'فشل Ping: '.$e->getMessage()];
  }
}

// ===== تطبيق WAN (Static / DHCP / PPPoE Client) =====
// ملاحظة: لا يتم إرجاع أو عرض أي أوامر RouterOS — فقط نتائج مفهومة.

function comms_mt_ensure_dns(RouterOSAPI $api, string $servers_csv): void {
  $servers_csv = trim($servers_csv);
  if($servers_csv === '') return;
  // RouterOS expects servers separated by comma
  $api->comm('/ip/dns/set', ['servers'=>$servers_csv, 'allow-remote-requests'=>'yes']);
}

function comms_mt_ensure_nat_masq(RouterOSAPI $api, string $wan_interface): void {
  // Ensure NAT masquerade exists on out-interface
  $rules = $api->comm('/ip/firewall/nat/print', ['?chain'=>'srcnat','?action'=>'masquerade','?out-interface'=>$wan_interface]);
  if(is_array($rules) && count($rules)>0) return;
  $api->comm('/ip/firewall/nat/add', [
    'chain'=>'srcnat',
    'out-interface'=>$wan_interface,
    'action'=>'masquerade',
    'comment'=>'AUTO: NAT WAN'
  ]);
}

function comms_mt_ensure_default_route(RouterOSAPI $api, string $gateway): void {
  $gateway = trim($gateway);
  if($gateway==='') return;
  $routes = $api->comm('/ip/route/print', ['?dst-address'=>'0.0.0.0/0']);
  if(is_array($routes) && count($routes)>0){
    // if gateway differs, update first route
    $id = $routes[0]['.id'] ?? null;
    $gw = $routes[0]['gateway'] ?? '';
    if($id && $gw !== $gateway){
      $api->comm('/ip/route/set', ['.id'=>$id,'gateway'=>$gateway]);
    }
    return;
  }
  $api->comm('/ip/route/add', ['dst-address'=>'0.0.0.0/0','gateway'=>$gateway,'comment'=>'AUTO: default route']);
}

function comms_mt_ensure_static_address(RouterOSAPI $api, string $wan_interface, string $ip_cidr): void {
  $ip_cidr = trim($ip_cidr);
  if($ip_cidr==='') return;
  $addr = $api->comm('/ip/address/print', ['?interface'=>$wan_interface,'?address'=>$ip_cidr]);
  if(is_array($addr) && count($addr)>0) return;
  // Remove other addresses on same interface? لا نحذف لتجنب التخريب. نضيف فقط إذا غير موجود.
  $api->comm('/ip/address/add', ['interface'=>$wan_interface,'address'=>$ip_cidr,'comment'=>'AUTO: WAN static']);
}

function comms_mt_ensure_dhcp_client(RouterOSAPI $api, string $wan_interface): void {
  $clients = $api->comm('/ip/dhcp-client/print', ['?interface'=>$wan_interface]);
  if(is_array($clients) && count($clients)>0){
    // enable first dhcp client
    $id = $clients[0]['.id'] ?? null;
    if($id){
      $api->comm('/ip/dhcp-client/set', ['.id'=>$id,'disabled'=>'no']);
    }
    return;
  }
  $api->comm('/ip/dhcp-client/add', ['interface'=>$wan_interface,'disabled'=>'no','comment'=>'AUTO: DHCP WAN']);
}

function comms_mt_ensure_pppoe_client(RouterOSAPI $api, string $wan_interface, string $user, string $pass): void {
  $user = trim($user);
  $pass = trim($pass);
  if($user==='') return;
  // Find existing pppoe-client on interface
  $clients = $api->comm('/interface/pppoe-client/print', ['?interface'=>$wan_interface]);
  if(is_array($clients) && count($clients)>0){
    $id = $clients[0]['.id'] ?? null;
    if($id){
      $api->comm('/interface/pppoe-client/set', [
        '.id'=>$id,
        'user'=>$user,
        'password'=>$pass,
        'disabled'=>'no',
        'add-default-route'=>'yes',
        'use-peer-dns'=>'yes'
      ]);
    }
    return;
  }
  $api->comm('/interface/pppoe-client/add', [
    'name'=>'pppoe-out1',
    'interface'=>$wan_interface,
    'user'=>$user,
    'password'=>$pass,
    'disabled'=>'no',
    'add-default-route'=>'yes',
    'use-peer-dns'=>'yes',
    'comment'=>'AUTO: PPPoE WAN'
  ]);
}

function comms_apply_wan(mysqli $conn, array $router, string $wan_interface, string $mode, array $params, string &$error): array {
  $error='';
  $wan_interface = trim($wan_interface);
  if($wan_interface==='') return ['ok'=>false,'error'=>'اكتب WAN Interface أولاً.'];
  $mode = strtolower(trim($mode));
  if(!in_array($mode,['static','dhcp','pppoe'],true)) return ['ok'=>false,'error'=>'نوع WAN غير صحيح.'];

  $api = comms_mt_connect($router,$error);
  if(!$api) return ['ok'=>false,'error'=>$error];

  try{
    // تطبيق حسب الوضع
    if($mode==='static'){
      $ip_cidr = trim($params['ip_cidr'] ?? '');
      $gw      = trim($params['gateway'] ?? '');
      $dns     = trim($params['dns'] ?? '');
      if($ip_cidr==='' || $gw===''){
        comms_mt_safe_disconnect($api);
        return ['ok'=>false,'error'=>'Static يحتاج IP/Mask (CIDR) و Gateway.'];
      }
      comms_mt_ensure_static_address($api,$wan_interface,$ip_cidr);
      comms_mt_ensure_default_route($api,$gw);
      comms_mt_ensure_dns($api,$dns);
      comms_mt_ensure_nat_masq($api,$wan_interface);
      comms_mt_safe_disconnect($api);
      return ['ok'=>true,'msg'=>'تم تطبيق Static WAN + Default Route + DNS + NAT (إن كانت ناقصة).'];
    }

    if($mode==='dhcp'){
      $dns = trim($params['dns'] ?? '');
      comms_mt_ensure_dhcp_client($api,$wan_interface);
      comms_mt_ensure_dns($api,$dns);
      comms_mt_ensure_nat_masq($api,$wan_interface);
      comms_mt_safe_disconnect($api);
      return ['ok'=>true,'msg'=>'تم تفعيل DHCP Client على WAN + NAT (إن كانت ناقصة).'];
    }

    // PPPoE client
    $user = trim($params['pppoe_user'] ?? '');
    $pass = trim($params['pppoe_pass'] ?? '');
    if($user===''){
      comms_mt_safe_disconnect($api);
      return ['ok'=>false,'error'=>'PPPoE يحتاج Username.'];
    }
    comms_mt_ensure_pppoe_client($api,$wan_interface,$user,$pass);
    // NAT على الواجهة الفيزيائية عادةً يكفي
    comms_mt_ensure_nat_masq($api,$wan_interface);
    comms_mt_safe_disconnect($api);
    return ['ok'=>true,'msg'=>'تم تطبيق PPPoE Client + NAT (إن كانت ناقصة).'];
  } catch(Exception $e){
    comms_mt_safe_disconnect($api);
    return ['ok'=>false,'error'=>'خطأ أثناء تطبيق WAN: '.$e->getMessage()];
  }
}

// ===== أدوات عامة (Auto Detect Interfaces) =====
// الهدف: اقتراح WAN/Transit/Trunk بدون WinBox وبأقل إدخال.
function comms_get_interfaces(mysqli $conn, array $router, string &$error): array {
  $error='';
  $api = comms_mt_connect($router,$error);
  if(!$api) return ['ok'=>false,'error'=>$error];
  try{
    $ifs = $api->comm('/interface/print');
    comms_mt_safe_disconnect($api);
    return ['ok'=>true,'data'=>$ifs];
  }catch(Exception $e){
    comms_mt_safe_disconnect($api);
    return ['ok'=>false,'error'=>'فشل قراءة الواجهات: '.$e->getMessage()];
  }
}

function comms_detect_best_wan_interface(mysqli $conn, array $router, string &$error): array {
  // heuristic:
  // 1) interface referenced by pppoe-client running
  // 2) interface referenced by dhcp-client bound
  // 3) interface that has an IP and default route gateway reachable
  $error='';
  $api = comms_mt_connect($router,$error);
  if(!$api) return ['ok'=>false,'error'=>$error];
  try{
    $candidates = [];
    // pppoe clients
    $pppoe = $api->comm('/interface/pppoe-client/print');
    if(is_array($pppoe)){
      foreach($pppoe as $c){
        if(($c['running'] ?? 'false')==='true'){
          $if = $c['interface'] ?? '';
          if($if!=='') $candidates[]=['if'=>$if,'score'=>90,'reason'=>'PPPoE client running'];
        }
      }
    }
    // dhcp clients
    $dhcp = $api->comm('/ip/dhcp-client/print');
    if(is_array($dhcp)){
      foreach($dhcp as $d){
        $st = strtolower($d['status'] ?? '');
        if(strpos($st,'bound')!==false){
          $if = $d['interface'] ?? '';
          if($if!=='') $candidates[]=['if'=>$if,'score'=>80,'reason'=>'DHCP bound'];
        }
      }
    }
    // static ip addresses
    $addrs = $api->comm('/ip/address/print');
    if(is_array($addrs)){
      foreach($addrs as $a){
        $if = $a['interface'] ?? '';
        $addr = $a['address'] ?? '';
        if($if!=='' && $addr!==''){
          $candidates[]=['if'=>$if,'score'=>60,'reason'=>'Has IP address'];
        }
      }
    }

    // choose best by score, keep unique with max score
    $best = null;
    $map = [];
    foreach($candidates as $c){
      $if=$c['if'];
      if(!isset($map[$if]) || $c['score']>$map[$if]['score']) $map[$if]=$c;
    }
    foreach($map as $c){
      if($best===null || $c['score']>$best['score']) $best=$c;
    }
    comms_mt_safe_disconnect($api);
    if(!$best){
      return ['ok'=>true,'data'=>['wan_interface'=>null,'confidence'=>'low','reason'=>'No clear WAN candidate']];
    }
    $conf = ($best['score']>=80)?'high':(($best['score']>=60)?'medium':'low');
    return ['ok'=>true,'data'=>['wan_interface'=>$best['if'],'confidence'=>$conf,'reason'=>$best['reason']]];
  }catch(Exception $e){
    comms_mt_safe_disconnect($api);
    return ['ok'=>false,'error'=>'فشل اكتشاف WAN: '.$e->getMessage()];
  }
}

// ===== VLAN Pool Plan (per sector) =====
function comms_suggest_pool_for_vlan(int $vlan_id): array {
  // Default suggestion: 10.10.<VID>.0/24 if VID <= 200 else 10.(VID%100).<VID/100>.0/24 fallback
  $vid = max(1, intval($vlan_id));
  if($vid >= 1 && $vid <= 200){
    $net = "10.10.$vid.0/24";
    return [
      'network_cidr'=>$net,
      'local_ip'=>"10.10.$vid.1",
      'pool_range'=>"10.10.$vid.2-10.10.$vid.254",
      'pool_name'=>"pool_vlan$vid",
      'server_name'=>"pppoe_vlan$vid",
      'service'=>"ISP-VLAN$vid"
    ];
  }
  $a = 10;
  $b = $vid % 100;
  $c = intdiv($vid, 100);
  if($b===0) $b=1;
  $net = "$a.$b.$c.0/24";
  return [
    'network_cidr'=>$net,
    'local_ip'=>"$a.$b.$c.1",
    'pool_range'=>"$a.$b.$c.2-$a.$b.$c.254",
    'pool_name'=>"pool_vlan$vid",
    'server_name'=>"pppoe_vlan$vid",
    'service'=>"ISP-VLAN$vid"
  ];
}

function comms_get_sectors(mysqli $conn): array {
  $rows=[];
  $r=$conn->query("SELECT * FROM sectors ORDER BY id ASC");
  if($r) while($row=$r->fetch_assoc()) $rows[]=$row;
  return $rows;
}

function comms_update_sector_pool(mysqli $conn, int $id, array $plan): void {
  $id=intval($id);
  $pool_name = $conn->real_escape_string($plan['pool_name'] ?? '');
  $pool_range= $conn->real_escape_string($plan['pool_range'] ?? '');
  $local_ip  = $conn->real_escape_string($plan['local_ip'] ?? '');
  $service   = $conn->real_escape_string($plan['service'] ?? '');
  $server_name = $conn->real_escape_string($plan['server_name'] ?? '');
  $conn->query("UPDATE sectors SET pppoe_pool_name='$pool_name', pppoe_pool_range='$pool_range', pppoe_local_ip='$local_ip', pppoe_service='$service', pppoe_server_name='$server_name' WHERE id=$id");
}

// Ensure pool exists
function comms_mt_ensure_pool(RouterOSAPI $api, string $name, string $range): void {
  $name=trim($name); $range=trim($range);
  if($name===''||$range==='') return;
  $r=$api->comm('/ip/pool/print', ['?name'=>$name]);
  if(is_array($r) && count($r)>0){
    $id=$r[0]['.id'] ?? null;
    if($id){
      $cur=$r[0]['ranges'] ?? '';
      if($cur!==$range){
        $api->comm('/ip/pool/set', ['.id'=>$id,'ranges'=>$range]);
      }
    }
    return;
  }
  $api->comm('/ip/pool/add', ['name'=>$name,'ranges'=>$range,'comment'=>'AUTO: PPPoE pool']);
}

function comms_mt_ensure_pppoe_server(RouterOSAPI $api, string $name, string $iface, string $service, string $local, string $pool, string $profile=''): void {
  $name=trim($name); $iface=trim($iface);
  if($iface==='') return;
  $q=['?interface'=>$iface];
  $r=$api->comm('/interface/pppoe-server/server/print', $q);
  if(is_array($r) && count($r)>0){
    $id=$r[0]['.id'] ?? null;
    if($id){
      $set=['.id'=>$id,'disabled'=>'no'];
      if($service!=='') $set['service-name']=$service;
      if($local!=='') $set['local-address']=$local;
      if($pool!=='') $set['remote-address']=$pool;
      if($profile!=='') $set['default-profile']=$profile;
      $api->comm('/interface/pppoe-server/server/set', $set);
    }
    return;
  }
  $payload=[
    'service-name'=>($service!==''?$service:'ISP'),
    'interface'=>$iface,
    'disabled'=>'no',
    'one-session-per-host'=>'yes'
  ];
  if($name!=='') $payload['name']=$name;
  if($local!=='') $payload['local-address']=$local;
  if($pool!=='')  $payload['remote-address']=$pool;
  if($profile!=='') $payload['default-profile']=$profile;
  $payload['comment']='AUTO: PPPoE server';
  $api->comm('/interface/pppoe-server/server/add', $payload);
}

function comms_apply_sector_pppoe(mysqli $conn, array $router, array $sector, string &$error): array {
  $error='';
  $api = comms_mt_connect($router,$error);
  if(!$api) return ['ok'=>false,'error'=>$error];
  try{
    $bridge = trim($sector['bridge_name'] ?? 'bridge-pppoe');
    $trunk  = trim($sector['trunk_interface'] ?? 'ether2');
    $vid    = intval($sector['vlan_id'] ?? 0);
    $vlanName = 'vlan'.$vid;

    // ensure bridge/vlan/bridge port
    $br=$api->comm('/interface/bridge/print', ['?name'=>$bridge]);
    if(!(is_array($br)&&count($br)>0)) $api->comm('/interface/bridge/add', ['name'=>$bridge]);
    $vl=$api->comm('/interface/vlan/print', ['?name'=>$vlanName]);
    if(!(is_array($vl)&&count($vl)>0)) $api->comm('/interface/vlan/add', ['name'=>$vlanName,'vlan-id'=>(string)$vid,'interface'=>$trunk]);
    $bp=$api->comm('/interface/bridge/port/print', ['?bridge'=>$bridge,'?interface'=>$vlanName]);
    if(!(is_array($bp)&&count($bp)>0)) $api->comm('/interface/bridge/port/add', ['bridge'=>$bridge,'interface'=>$vlanName]);

    // ensure pool
    $poolName = trim($sector['pppoe_pool_name'] ?? '');
    $poolRange= trim($sector['pppoe_pool_range'] ?? '');
    $localIp  = trim($sector['pppoe_local_ip'] ?? '');
    $service  = trim($sector['pppoe_service'] ?? '');
    $serverName = trim($sector['pppoe_server_name'] ?? '');
    $profile  = trim($sector['pppoe_profile'] ?? '');
    if($poolName==='' || $poolRange==='' || $localIp===''){
      comms_mt_safe_disconnect($api);
      return ['ok'=>false,'error'=>'إعدادات Pool/Local IP ناقصة لهذا القطاع. استخدم زر "اقتراح" أولاً.'];
    }
    comms_mt_ensure_pool($api,$poolName,$poolRange);
    // pppoe server per vlan (interface=vlanX)
    comms_mt_ensure_pppoe_server($api,$serverName,$vlanName,$service,$localIp,$poolName,$profile);

    comms_mt_safe_disconnect($api);
    return ['ok'=>true,'msg'=>'تم تطبيق VLAN + Pool + PPPoE Server لهذا القطاع (Ensure).'];
  }catch(Exception $e){
    comms_mt_safe_disconnect($api);
    return ['ok'=>false,'error'=>'فشل تطبيق PPPoE للقطاع: '.$e->getMessage()];
  }
}


/* ============================================================
 * Smart Wizard: Auto-detect + Apply Transit + Test Checklist
 * ============================================================ */

function comms_api_by_router_id(mysqli $conn, int $router_id) {
    $r = function_exists('comms_router_get') ? comms_router_get($conn, $router_id) : comms_get_router($conn, $router_id);
    if (!$r) return null;
    $err = '';
    $api = ua_mt_connect_config($r, $err, 0.8);
    return ($api instanceof RouterOSAPI) ? $api : null;
}

function comms_action_key(string $action, int $router_id, string $scope): string {
    return sha1($action.'|'.$router_id.'|'.$scope);
}

function comms_action_done(mysqli $conn, string $key): bool {
    $stmt = $conn->prepare("SELECT id FROM action_log WHERE action_key=? LIMIT 1");
    $stmt->bind_param("s",$key);
    $stmt->execute();
    $res = $stmt->get_result();
    return ($res && $res->num_rows>0);
}

function comms_action_mark(mysqli $conn, string $key, string $action, string $details=''): void {
    comms_ensure_tables($conn);
    $hash = hash('sha256', $action.'|'.$details);
    $uid = intval($_SESSION['user_id'] ?? ($_SESSION['admin_id'] ?? 0));
    $createdBy = $uid > 0 ? $uid : null;
    $stmt = $conn->prepare("INSERT IGNORE INTO action_log(action_key, payload_hash, created_by, action, details) VALUES(?,?,?,?,?)");
    if ($stmt) {
      $stmt->bind_param("ssiss", $key, $hash, $createdBy, $action, $details);
      @$stmt->execute();
      return;
    }

    $keyE = $conn->real_escape_string($key);
    $hashE = $conn->real_escape_string($hash);
    $uidSql = $createdBy === null ? 'NULL' : (string)(int)$createdBy;
    @$conn->query("INSERT IGNORE INTO action_log(action_key,payload_hash,created_by) VALUES('$keyE','$hashE',$uidSql)");
}

if (!function_exists('mt_cmd')) {
  function mt_cmd(RouterOSAPI $api, array $sentence): array {
    try {
      $rows = $api->command($sentence);
      return array_values(array_filter($rows, static function($row) {
        return is_array($row) && (($row['.type'] ?? '') === '!re');
      }));
    } catch (Throwable $e) {
      return [];
    }
  }
}

if (!function_exists('mt_route_print')) {
  function mt_route_print(RouterOSAPI $api): array {
    return mt_cmd($api, ['/ip/route/print']);
  }
}

if (!function_exists('mt_default_route_exists')) {
  function mt_default_route_exists(array $routes): bool {
    foreach ($routes as $r) {
      if (($r['dst-address'] ?? '') === '0.0.0.0/0' && ($r['disabled'] ?? 'false') !== 'true') return true;
    }
    return false;
  }
}

if (!function_exists('mt_ping')) {
  function mt_ping(RouterOSAPI $api, string $target, int $count = 2): bool {
    $target = trim($target);
    if ($target === '') return false;
    $rows = mt_cmd($api, ['/ping', '=address='.$target, '=count='.max(1, $count)]);
    foreach ($rows as $row) {
      $received = (int)($row['received'] ?? 0);
      $loss = (string)($row['packet-loss'] ?? '');
      $status = strtolower((string)($row['status'] ?? ''));
      if ($received > 0 || ($loss !== '' && $loss !== '100%') || ($status !== '' && strpos($status, 'timeout') === false)) {
        return true;
      }
    }
    return false;
  }
}

if (!function_exists('mt_autodetect_transit_trunk')) {
  function mt_autodetect_transit_trunk(RouterOSAPI $api1, RouterOSAPI $api2): array {
    $ifaceRows = static function(RouterOSAPI $api): array {
      $rows = mt_cmd($api, ['/interface/print']);
      $names = [];
      foreach ($rows as $row) {
        $name = trim((string)($row['name'] ?? ''));
        if ($name !== '' && ($row['disabled'] ?? 'false') !== 'true') $names[] = $name;
      }
      return array_values(array_unique($names));
    };
    $pick = static function(array $names, array $preferred, array $exclude = []): string {
      foreach ($preferred as $cand) {
        if (in_array($cand, $names, true) && !in_array($cand, $exclude, true)) return $cand;
      }
      foreach ($names as $name) {
        if (!in_array($name, $exclude, true)) return $name;
      }
      return '';
    };

    $wanRaw = function_exists('mt_autodetect_wan_iface') ? mt_autodetect_wan_iface($api1) : '';
    $wan = is_array($wanRaw) ? (string)($wanRaw['wan_iface'] ?? $wanRaw['wan_interface'] ?? '') : (string)$wanRaw;
    $r1 = $ifaceRows($api1);
    $r2 = $ifaceRows($api2);

    $transit1 = $pick($r1, ['ether2','sfp-sfpplus1','sfp1','ether3','ether4','ether5'], [$wan]);
    $transit2 = $pick($r2, ['ether1','sfp-sfpplus1','sfp1','ether2'], []);
    $trunk2 = $pick($r2, ['ether2','ether3','sfp-sfpplus2','sfp2','bridge-pppoe'], [$transit2]);

    return [
      'wan_uplink' => $wan,
      'transit_uplink' => $transit1,
      'transit_pppoe' => $transit2,
      'trunk_pppoe' => $trunk2,
    ];
  }
}

function comms_detect_topology(mysqli $conn): array {
    $upl = comms_router_by_role($conn,'uplink');
    $ppp = comms_router_by_role($conn,'pppoe');
    if (!$upl || !$ppp) return ['ok'=>false,'msg'=>'يجب تعريف Router uplink و Router pppoe أولاً | Define uplink & pppoe routers first'];

    $api1 = comms_api_by_router_id($conn, intval($upl['id']));
    $api2 = comms_api_by_router_id($conn, intval($ppp['id']));
    if (!$api1) return ['ok'=>false,'msg'=>'فشل اتصال Router1 | Router1 API connect failed'];
    if (!$api2) { $api1->disconnect(); return ['ok'=>false,'msg'=>'فشل اتصال Router2 | Router2 API connect failed']; }

    $wan1 = mt_autodetect_wan_iface($api1);
    $tt = mt_autodetect_transit_trunk($api1, $api2);

    $api1->disconnect(); $api2->disconnect();
    return ['ok'=>true,'wan'=>$wan1,'links'=>$tt,'uplink'=>$upl,'pppoe'=>$ppp];
}

/**
 * Apply Transit between Router1 and Router2:
 * - set IPs on transit interfaces
 * - add route on Router1 to PPPoE networks (from sectors pools)
 * - add default route on Router2 via Router1 transit
 */
function comms_apply_transit(mysqli $conn, array $opts): array {
    comms_migrate($conn);

    $upl = comms_router_by_role($conn,'uplink');
    $ppp = comms_router_by_role($conn,'pppoe');
    if (!$upl || !$ppp) return ['ok'=>false,'msg'=>'Router roles غير معرّفة | Roles not set'];

    $api1 = comms_api_by_router_id($conn, intval($upl['id']));
    $api2 = comms_api_by_router_id($conn, intval($ppp['id']));
    if (!$api1 || !$api2) {
        if ($api1) $api1->disconnect();
        if ($api2) $api2->disconnect();
        return ['ok'=>false,'msg'=>'تعذر الاتصال بالراوترات | Cannot connect routers'];
    }

    $transit1 = $opts['transit_uplink'] ?? '';
    $transit2 = $opts['transit_pppoe'] ?? '';
    if ($transit1==='' || $transit2==='') {
        $api1->disconnect(); $api2->disconnect();
        return ['ok'=>false,'msg'=>'لم يتم تحديد منافذ Transit | Transit ports not detected'];
    }

    // Default transit addresses
    $r1_ip = $opts['r1_ip'] ?? '10.255.255.1/30';
    $r2_ip = $opts['r2_ip'] ?? '10.255.255.2/30';
    $r1_gw = '10.255.255.1';
    $r2_gw = '10.255.255.1'; // router2 default via router1 transit

    $msgs = [];

    // Ensure IP address on Router1 transit interface
    $key = comms_action_key('TRANSIT_IP_R1', intval($upl['id']), $transit1.'|'.$r1_ip);
    if (!comms_action_done($conn,$key)) {
        mt_cmd($api1, ['/ip/address/add', '=address='.$r1_ip, '=interface='.$transit1, '=comment=Transit-R1']);
        comms_action_mark($conn,$key,'TRANSIT_IP_R1',$transit1.' '.$r1_ip);
        $msgs[]='✅ تم ضبط IP الترانزيت على Router1 | Transit IP set on Router1';
    } else {
        $msgs[]='⚠️ تم تخطي IP Router1 لأنه منفذ مسبقاً | Skipped Router1 transit IP (already applied)';
    }

    // Ensure IP on Router2 transit interface
    $key = comms_action_key('TRANSIT_IP_R2', intval($ppp['id']), $transit2.'|'.$r2_ip);
    if (!comms_action_done($conn,$key)) {
        mt_cmd($api2, ['/ip/address/add', '=address='.$r2_ip, '=interface='.$transit2, '=comment=Transit-R2']);
        comms_action_mark($conn,$key,'TRANSIT_IP_R2',$transit2.' '.$r2_ip);
        $msgs[]='✅ تم ضبط IP الترانزيت على Router2 | Transit IP set on Router2';
    } else {
        $msgs[]='⚠️ تم تخطي IP Router2 لأنه منفذ مسبقاً | Skipped Router2 transit IP (already applied)';
    }

    // Ensure default route on Router2 via Router1 transit
    $key = comms_action_key('R2_DEFAULT_ROUTE', intval($ppp['id']), $r2_gw);
    if (!comms_action_done($conn,$key)) {
        mt_cmd($api2, ['/ip/route/add', '=dst-address=0.0.0.0/0', '=gateway='.$r2_gw, '=comment=Default-to-R1']);
        comms_action_mark($conn,$key,'R2_DEFAULT_ROUTE',$r2_gw);
        $msgs[]='✅ تم إضافة Default Route على Router2 | Default route added on Router2';
    } else {
        $msgs[]='⚠️ تم تخطي Default Route على Router2 | Skipped Router2 default route';
    }

    // Router1 routes to PPPoE networks: gather unique /24 from sectors local ip (10.10.X.1) -> dst 10.10.X.0/24
    $sectors = comms_sectors_list($conn);
    $dsts = [];
    foreach ($sectors as $s) {
        $local = $s['pppoe_local_ip'] ?? '';
        if ($local) {
            // local like 10.10.20.1
            $parts = explode('.',$local);
            if (count($parts)===4) {
                $dsts[] = $parts[0].'.'.$parts[1].'.'.$parts[2].'.0/24';
            }
        }
    }
    $dsts = array_values(array_unique($dsts));
    foreach ($dsts as $dst) {
        $key = comms_action_key('R1_ROUTE', intval($upl['id']), $dst.'|10.255.255.2');
        if (!comms_action_done($conn,$key)) {
            mt_cmd($api1, ['/ip/route/add', '=dst-address='.$dst, '=gateway=10.255.255.2', '=comment=To-PPPoE']);
            comms_action_mark($conn,$key,'R1_ROUTE',$dst);
            $msgs[]='✅ Route مضافة على Router1 للشبكة '.$dst.' | Route added for '.$dst;
        } else {
            $msgs[]='⚠️ Route موجود مسبقاً للشبكة '.$dst.' | Route already exists for '.$dst;
        }
    }

    $api1->disconnect(); $api2->disconnect();

    return ['ok'=>true,'msgs'=>$msgs, 'transit'=>[$transit1,$transit2]];
}

function comms_test_all(mysqli $conn): array {
    $upl = comms_router_by_role($conn,'uplink');
    $ppp = comms_router_by_role($conn,'pppoe');
    if (!$upl || !$ppp) return ['ok'=>false,'msg'=>'يجب تعريف الراوترات | Routers not defined'];

    $api1 = comms_api_by_router_id($conn, intval($upl['id']));
    $api2 = comms_api_by_router_id($conn, intval($ppp['id']));
    if (!$api1 || !$api2) {
        if ($api1) $api1->disconnect();
        if ($api2) $api2->disconnect();
        return ['ok'=>false,'msg'=>'فشل الاتصال بالراوترات | API connection failed'];
    }

    $results = [];

    // WAN detect
    $wan = mt_autodetect_wan_iface($api1);
    $wanName = is_array($wan) ? (string)($wan['wan_iface'] ?? $wan['wan_interface'] ?? '') : (string)$wan;
    $wanMode = is_array($wan) ? (string)($wan['mode_detected'] ?? 'auto') : 'auto';
    $results[] = ['k'=>'WAN Detect','ar'=>'اكتشاف WAN','ok'=>$wanName !== '','v'=>($wanName !== '' ? $wanName : 'غير معروف').' / '.$wanMode];

    // Ping gateway
    $routes = mt_route_print($api1);
    $gw = null;
    foreach ($routes as $r) {
        if (($r['dst-address']??'')==='0.0.0.0/0') { $gw = $r['gateway'] ?? null; break; }
    }
    if ($gw) {
        $ok = mt_ping($api1, $gw, 2);
        $results[]=['k'=>'Ping GW','ar'=>'Ping للبوابة','ok'=>$ok,'v'=>$gw];
    } else {
        $results[]=['k'=>'Ping GW','ar'=>'Ping للبوابة','ok'=>false,'v'=>'لا يوجد Default Route'];
    }

    // Ping internet
    $ok1 = mt_ping($api1,'1.1.1.1',2);
    $results[]=['k'=>'Ping 1.1.1.1','ar'=>'Ping للإنترنت','ok'=>$ok1,'v'=>'1.1.1.1'];

    // Router2 default route exists
    $r2routes = mt_route_print($api2);
    $results[]=['k'=>'R2 Default Route','ar'=>'Default Route على Router2','ok'=>mt_default_route_exists($r2routes),'v'=>mt_default_route_exists($r2routes)?'موجود':'غير موجود'];

    // PPPoE servers status: per sector
    $sectors = comms_sectors_list($conn);
    $srv = mt_cmd($api2, ['/interface/pppoe-server/server/print']);
    $srv_names = [];
    foreach ($srv as $s) { $srv_names[] = $s['service-name'] ?? ($s['name'] ?? ''); }
    $running = 0;
    foreach ($srv as $s) if (($s['disabled']??'')!=='true') $running++;
    $results[]=['k'=>'PPPoE Servers','ar'=>'خدمات PPPoE','ok'=>($running>0),'v'=>$running.' servers'];

    // Active sessions
    $active = mt_cmd($api2, ['/ppp/active/print']);
    $results[]=['k'=>'Active Sessions','ar'=>'جلسات متصلة','ok'=>true,'v'=>count($active).' online'];

    $api1->disconnect(); $api2->disconnect();

    return ['ok'=>true,'results'=>$results];
}
