<?php
// admin/subscribers_import_id_cards.php
if (session_status() === PHP_SESSION_NONE) session_start();
if (!ob_get_level()) ob_start();

$page_title = 'استيراد البطاقات الشخصية';
$GLOBALS['UA_POPUP'] = true;
if (!defined('UA_POPUP')) define('UA_POPUP', true);
if (!defined('UA_EMBED')) define('UA_EMBED', true);

require_once __DIR__ . '/auth.php';
require_once __DIR__ . '/helpers.php';
require_once __DIR__ . '/db_bootstrap.php';
require_once __DIR__ . '/super_admin_guard.php';

require_login();
if (function_exists('require_perm')) require_perm('can_edit_subscriber');
if (function_exists('ensure_db')) ensure_db(true);
$conn = $GLOBALS['conn'] ?? null;
if (!($conn instanceof mysqli)) { http_response_code(500); die('Database connection error'); }

function siid_h($v): string { return htmlspecialchars((string)$v, ENT_QUOTES, 'UTF-8'); }
function siid_col_exists(mysqli $conn, string $table, string $col): bool {
  static $cache = [];
  $key = $table . '|' . $col;
  if (array_key_exists($key, $cache)) return $cache[$key];
  $t = $conn->real_escape_string($table);
  $c = $conn->real_escape_string($col);
  $rs = @$conn->query("SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='$t' AND COLUMN_NAME='$c' LIMIT 1");
  return $cache[$key] = ($rs && $rs->num_rows > 0);
}
function siid_norm_ar(string $v): string {
  $v = trim($v);
  $v = str_replace("\xC2\xA0", ' ', $v);
  $v = preg_replace('/[\x{064B}-\x{065F}\x{0670}\x{0640}]/u', '', $v);
  $v = strtr($v, ['أ'=>'ا','إ'=>'ا','آ'=>'ا','ٱ'=>'ا','ى'=>'ي','ئ'=>'ي','ؤ'=>'و','ة'=>'ه']);
  $v = preg_replace('/\s+/u', ' ', $v);
  return trim(mb_strtolower($v, 'UTF-8'));
}
function siid_norm_digits(string $v): string { return preg_replace('/\D+/', '', $v) ?? ''; }
function siid_norm_key(string $v, string $field): string {
  $v = trim((string)$v);
  $v = preg_replace('/\.[a-z0-9]{2,5}$/i', '', $v);
  $v = preg_replace('/\s+/u', ' ', $v);
  if (in_array($field, ['phone','national_id','id'], true)) return siid_norm_digits($v);
  if ($field === 'full_name') return siid_norm_ar($v);
  return mb_strtolower(preg_replace('/\s+/u', '', preg_replace('/@.*/', '', $v)), 'UTF-8');
}
function siid_file_key(string $filename, string $field): string {
  $base = pathinfo($filename, PATHINFO_FILENAME);
  $base = preg_replace('/(^|[\s_\-.])(front|back|face|rear|f|b|امامي|أمامي|وجه|خلفي|ظهر)([\s_\-.]|$)/iu', ' ', $base);
  $base = str_replace(['_', '-', '.', '+'], ' ', (string)$base);
  return siid_norm_key($base, $field);
}
function siid_detect_side(string $filename): string {
  $name = siid_norm_ar(pathinfo($filename, PATHINFO_FILENAME));
  if (preg_match('/(^|[\s_\-.])(front|face|f|امامي|وجه)([\s_\-.]|$)/iu', $filename) || mb_strpos($name, 'امامي') !== false || mb_strpos($name, 'وجه') !== false) return 'front';
  if (preg_match('/(^|[\s_\-.])(back|rear|b|خلفي|ظهر)([\s_\-.]|$)/iu', $filename) || mb_strpos($name, 'خلفي') !== false || mb_strpos($name, 'ظهر') !== false) return 'back';
  return 'unknown';
}
function siid_side_label(string $side): string {
  if ($side === 'front') return 'الوجه الأمامي';
  if ($side === 'back') return 'الوجه الخلفي';
  if ($side === 'both') return 'الوجهين معا';
  return 'غير محدد';
}
function siid_key_fields(mysqli $conn): array {
  $fields = ['id' => '# ID'];
  foreach ([
    'national_id' => 'الرقم الوطني',
    'phone' => 'رقم الجوال',
    'ppp_username' => 'حساب PPPoE',
    'username' => 'اسم المستخدم',
    'document_number' => 'رقم الوثيقة',
    'record_number' => 'رقم القيد',
    'full_name' => 'الاسم الكامل',
  ] as $col => $label) {
    if (siid_col_exists($conn, 'subscribers', $col)) $fields[$col] = $label;
  }
  return $fields;
}
function siid_existing_card(array $row, string $side): string {
  $keys = $side === 'front' ? ['id_card_front','id_front'] : ['id_card_back','id_back'];
  foreach ($keys as $k) {
    $v = trim((string)($row[$k] ?? ''));
    if ($v !== '') return $v;
  }
  return '';
}
function siid_fetch_subscribers(mysqli $conn): array {
  $cols = ['id'];
  foreach (['full_name','username','ppp_username','phone','national_id','document_number','record_number','id_card_front','id_card_back','id_front','id_back'] as $col) {
    if (siid_col_exists($conn, 'subscribers', $col)) $cols[] = $col;
  }
  $where = siid_col_exists($conn, 'subscribers', 'is_deleted') ? 'WHERE COALESCE(is_deleted,0)=0' : '';
  $rs = @$conn->query('SELECT `' . implode('`,`', array_unique($cols)) . "` FROM subscribers $where ORDER BY id DESC");
  $rows = [];
  if ($rs) while ($r = $rs->fetch_assoc()) $rows[] = $r;
  return $rows;
}
function siid_index_subscribers(array $subs, string $field): array {
  $index = [];
  foreach ($subs as $s) {
    $raw = $field === 'id' ? (string)($s['id'] ?? '') : (string)($s[$field] ?? '');
    $key = siid_norm_key($raw, $field);
    if ($key === '') continue;
    $index[$key][] = $s;
  }
  return $index;
}
function siid_match_subscriber(array $index, string $key, bool $allowFuzzy): array {
  if ($key !== '' && isset($index[$key])) {
    return count($index[$key]) === 1
      ? ['status'=>'exact','subscriber'=>$index[$key][0],'score'=>100]
      : ['status'=>'ambiguous','matches'=>$index[$key],'score'=>100];
  }
  if (!$allowFuzzy || $key === '' || mb_strlen($key, 'UTF-8') < 5) return ['status'=>'unmatched'];
  $hits = [];
  foreach ($index as $candidate => $rows) {
    if (abs(mb_strlen($candidate, 'UTF-8') - mb_strlen($key, 'UTF-8')) > 1) continue;
    if (levenshtein($candidate, $key) <= 1) {
      foreach ($rows as $r) $hits[] = $r;
    }
  }
  if (count($hits) === 1) return ['status'=>'fuzzy','subscriber'=>$hits[0],'score'=>90];
  if (count($hits) > 1) return ['status'=>'ambiguous','matches'=>$hits,'score'=>90];
  return ['status'=>'unmatched'];
}
function siid_tmp_root(): string {
  $dir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'ua_id_card_imports';
  if (!is_dir($dir)) @mkdir($dir, 0775, true);
  return $dir;
}
function siid_clean_dir(string $dir): void {
  if (!is_dir($dir)) return;
  $base = realpath($dir);
  $root = realpath(siid_tmp_root());
  if (!$base || !$root || strpos($base, $root) !== 0) return;
  $it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($base, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST);
  foreach ($it as $p) { $p->isDir() ? @rmdir($p->getPathname()) : @unlink($p->getPathname()); }
  @rmdir($base);
}
function siid_extract_zip(string $zipPath, string $target): array {
  if (!is_dir($target)) @mkdir($target, 0775, true);
  $files = [];
  $allowed = ['jpg','jpeg','png','webp','pdf'];
  if (class_exists('ZipArchive')) {
    $zip = new ZipArchive();
    if ($zip->open($zipPath) !== true) throw new RuntimeException('تعذر فتح ملف ZIP.');
    for ($i = 0; $i < $zip->numFiles; $i++) {
      $name = (string)$zip->getNameIndex($i);
      if ($name === '' || substr($name, -1) === '/') continue;
      $base = basename(str_replace('\\', '/', $name));
      $ext = strtolower(pathinfo($base, PATHINFO_EXTENSION));
      if (!in_array($ext, $allowed, true)) continue;
      $safe = preg_replace('/[^a-zA-Z0-9_\-.]+/', '_', $base) ?: ('card_' . $i . '.' . $ext);
      $dest = $target . DIRECTORY_SEPARATOR . $i . '_' . $safe;
      $in = $zip->getStream($name);
      if (!$in) continue;
      $out = @fopen($dest, 'wb');
      if (!$out) { fclose($in); continue; }
      stream_copy_to_stream($in, $out, 12 * 1024 * 1024);
      fclose($in); fclose($out);
      $files[] = ['original'=>$base, 'path'=>$dest, 'ext'=>$ext, 'size'=>@filesize($dest) ?: 0];
    }
    $zip->close();
    return $files;
  }

  if (!class_exists('PharData')) throw new RuntimeException('لا توجد إضافة لفك ZIP في PHP. فعّل ZipArchive.');
  try {
    $phar = new PharData($zipPath);
    $i = 0;
    $it = new RecursiveIteratorIterator($phar);
    foreach ($it as $entry) {
      if (!$entry->isFile()) continue;
      $base = basename(str_replace('\\', '/', $entry->getPathName()));
      $ext = strtolower(pathinfo($base, PATHINFO_EXTENSION));
      if (!in_array($ext, $allowed, true)) continue;
      $safe = preg_replace('/[^a-zA-Z0-9_\-.]+/', '_', $base) ?: ('card_' . $i . '.' . $ext);
      $dest = $target . DIRECTORY_SEPARATOR . $i . '_' . $safe;
      $in = @fopen($entry->getPathName(), 'rb');
      if (!$in) continue;
      $out = @fopen($dest, 'wb');
      if (!$out) { fclose($in); continue; }
      stream_copy_to_stream($in, $out, 12 * 1024 * 1024);
      fclose($in); fclose($out);
      $files[] = ['original'=>$base, 'path'=>$dest, 'ext'=>$ext, 'size'=>@filesize($dest) ?: 0];
      $i++;
    }
  } catch (Throwable $e) {
    throw new RuntimeException('تعذر فك ملف ZIP: ' . $e->getMessage());
  }
  return $files;
}
function siid_json(array $data): void {
  while (ob_get_level()) @ob_end_clean();
  header('Content-Type: application/json; charset=utf-8');
  echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
  exit;
}
function siid_chunk_dir(string $token): string {
  return siid_tmp_root() . DIRECTORY_SEPARATOR . 'chunk_' . preg_replace('/[^a-f0-9]/i', '', $token);
}
function siid_public_dest(array $file, int $sid, string $side): array {
  $dir = __DIR__ . '/assets/id_cards';
  if (!is_dir($dir)) @mkdir($dir, 0775, true);
  try { $rand = bin2hex(random_bytes(4)); } catch (Throwable $e) { $rand = substr(md5(uniqid('', true)), 0, 8); }
  $ext = strtolower((string)($file['ext'] ?? 'jpg'));
  $name = 'bulk_' . $sid . '_' . $side . '_' . date('Ymd_His') . '_' . $rand . '.' . $ext;
  return [$dir . '/' . $name, 'assets/id_cards/' . $name];
}
function siid_update_card(mysqli $conn, int $sid, string $side, string $rel): void {
  $sets = [];
  if ($side === 'front' || $side === 'both') {
    if (siid_col_exists($conn, 'subscribers', 'id_card_front')) $sets[] = "`id_card_front`='" . $conn->real_escape_string($rel) . "'";
    if (siid_col_exists($conn, 'subscribers', 'id_front')) $sets[] = "`id_front`='" . $conn->real_escape_string($rel) . "'";
  }
  if ($side === 'back' || $side === 'both') {
    if (siid_col_exists($conn, 'subscribers', 'id_card_back')) $sets[] = "`id_card_back`='" . $conn->real_escape_string($rel) . "'";
    if (siid_col_exists($conn, 'subscribers', 'id_back')) $sets[] = "`id_back`='" . $conn->real_escape_string($rel) . "'";
  }
  if (siid_col_exists($conn, 'subscribers', 'updated_at')) $sets[] = "`updated_at`=NOW()";
  if (!$sets) throw new RuntimeException('أعمدة صور البطاقة غير موجودة.');
  @$conn->query('UPDATE subscribers SET ' . implode(',', $sets) . ' WHERE id=' . (int)$sid . ' LIMIT 1');
  if ($conn->error) throw new RuntimeException($conn->error);
}
function siid_build_preview(mysqli $conn, array $files, string $keyField, string $imageType, string $facesMode, bool $replaceExisting): array {
  $subs = siid_fetch_subscribers($conn);
  $index = siid_index_subscribers($subs, $keyField);
  $items = [];
  foreach ($files as $file) {
    $side = ($imageType === 'both' && $facesMode === 'separate') ? siid_detect_side($file['original']) : $imageType;
    $key = siid_file_key($file['original'], $keyField);
    $match = siid_match_subscriber($index, $key, true);
    $item = ['file'=>$file, 'key'=>$key, 'side'=>$side, 'status'=>$match['status'], 'score'=>$match['score'] ?? 0, 'subscriber'=>$match['subscriber'] ?? null, 'matches'=>$match['matches'] ?? []];
    if ($imageType === 'both' && $facesMode === 'separate' && $side === 'unknown') $item['status'] = 'side_unknown';
    if (!empty($item['subscriber']) && !$replaceExisting) {
      $frontExists = siid_existing_card($item['subscriber'], 'front') !== '';
      $backExists = siid_existing_card($item['subscriber'], 'back') !== '';
      if (($side === 'front' && $frontExists) || ($side === 'back' && $backExists) || ($side === 'both' && ($frontExists || $backExists))) $item['status'] = 'has_existing';
    }
    $items[] = $item;
  }
  $counts = ['total'=>count($items),'ready'=>0,'exact'=>0,'fuzzy'=>0,'unmatched'=>0,'ambiguous'=>0,'side_unknown'=>0,'has_existing'=>0];
  foreach ($items as $it) {
    if ($it['status'] === 'exact') { $counts['exact']++; $counts['ready']++; }
    elseif ($it['status'] === 'fuzzy') { $counts['fuzzy']++; $counts['ready']++; }
    elseif (isset($counts[$it['status']])) $counts[$it['status']]++;
    else $counts['unmatched']++;
  }
  $missing = [];
  foreach ($subs as $s) {
    $frontMissing = siid_existing_card($s, 'front') === '';
    $backMissing = siid_existing_card($s, 'back') === '';
    $need = ($imageType === 'front' && $frontMissing) || ($imageType === 'back' && $backMissing) || ($imageType === 'both' && ($frontMissing || $backMissing));
    if ($need) $missing[] = $s + ['missing_front'=>$frontMissing, 'missing_back'=>$backMissing];
  }
  return ['items'=>$items,'counts'=>$counts,'missing'=>$missing,'key_field'=>$keyField,'image_type'=>$imageType,'faces_mode'=>$facesMode,'replace_existing'=>$replaceExisting];
}

$msg = '';
$err = '';
$preview = null;
$keyFields = siid_key_fields($conn);
$uploadMaxText = (string)ini_get('upload_max_filesize');
$postMaxText = (string)ini_get('post_max_size');
if (!isset($_SESSION['siid_imports']) || !is_array($_SESSION['siid_imports'])) $_SESSION['siid_imports'] = [];

if (($_GET['action'] ?? '') === 'clear_import_silent') {
  foreach ($_SESSION['siid_imports'] as $x) if (!empty($x['dir'])) siid_clean_dir((string)$x['dir']);
  $_SESSION['siid_imports'] = [];
  exit;
}

try {
  if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'chunk_init') {
    $token = bin2hex(random_bytes(8));
    $total = max(1, (int)($_POST['total_chunks'] ?? 0));
    $name = basename((string)($_POST['filename'] ?? 'cards.zip'));
    if (strtolower(pathinfo($name, PATHINFO_EXTENSION)) !== 'zip') siid_json(['ok'=>false,'message'=>'الملف يجب أن يكون ZIP.']);
    $dir = siid_chunk_dir($token);
    if (!is_dir($dir)) @mkdir($dir, 0775, true);
    $_SESSION['siid_chunk_uploads'][$token] = ['dir'=>$dir,'total'=>$total,'filename'=>$name,'created_at'=>time()];
    siid_json(['ok'=>true,'token'=>$token]);
  } elseif ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'chunk_upload') {
    $token = preg_replace('/[^a-f0-9]/i', '', (string)($_POST['token'] ?? ''));
    $state = $_SESSION['siid_chunk_uploads'][$token] ?? null;
    if (!$state) siid_json(['ok'=>false,'message'=>'انتهت جلسة رفع الملف.']);
    $index = (int)($_POST['index'] ?? -1);
    if ($index < 0 || $index >= (int)$state['total']) siid_json(['ok'=>false,'message'=>'رقم الدفعة غير صالح.']);
    if (empty($_FILES['chunk']) || (int)($_FILES['chunk']['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) siid_json(['ok'=>false,'message'=>'فشل رفع دفعة من الملف.']);
    $dest = rtrim((string)$state['dir'], '/\\') . DIRECTORY_SEPARATOR . $index . '.part';
    if (!@move_uploaded_file((string)$_FILES['chunk']['tmp_name'], $dest)) siid_json(['ok'=>false,'message'=>'تعذر حفظ دفعة من الملف.']);
    siid_json(['ok'=>true,'index'=>$index]);
  } elseif ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'preview_chunked') {
    $uploadToken = preg_replace('/[^a-f0-9]/i', '', (string)($_POST['upload_token'] ?? ''));
    $state = $_SESSION['siid_chunk_uploads'][$uploadToken] ?? null;
    if (!$state) throw new RuntimeException('انتهت جلسة رفع الملف. أعد اختيار الملف.');
    $token = bin2hex(random_bytes(8));
    $dir = siid_tmp_root() . DIRECTORY_SEPARATOR . $token;
    if (!is_dir($dir)) @mkdir($dir, 0775, true);
    $zipForRead = $dir . DIRECTORY_SEPARATOR . 'source.zip';
    $out = @fopen($zipForRead, 'wb');
    if (!$out) throw new RuntimeException('تعذر تجهيز ملف ZIP للقراءة.');
    for ($i = 0; $i < (int)$state['total']; $i++) {
      $part = rtrim((string)$state['dir'], '/\\') . DIRECTORY_SEPARATOR . $i . '.part';
      if (!is_file($part)) { fclose($out); throw new RuntimeException('ملف ZIP لم يكتمل رفعه.'); }
      $in = @fopen($part, 'rb');
      if (!$in) { fclose($out); throw new RuntimeException('تعذر قراءة دفعة من ملف ZIP.'); }
      stream_copy_to_stream($in, $out);
      fclose($in);
    }
    fclose($out);
    siid_clean_dir((string)$state['dir']);
    unset($_SESSION['siid_chunk_uploads'][$uploadToken]);
    $keyField = (string)($_POST['key_field'] ?? 'national_id');
    if (!isset($keyFields[$keyField])) $keyField = 'id';
    $imageType = (string)($_POST['image_type'] ?? 'front');
    if (!in_array($imageType, ['front','back','both'], true)) $imageType = 'front';
    $facesMode = (string)($_POST['faces_mode'] ?? 'same');
    if (!in_array($facesMode, ['same','separate'], true)) $facesMode = 'same';
    $replaceExisting = !empty($_POST['replace_existing']);
    $files = siid_extract_zip($zipForRead, $dir);
    if (!$files) throw new RuntimeException('لم يتم العثور على صور مدعومة داخل ZIP.');
    $preview = siid_build_preview($conn, $files, $keyField, $imageType, $facesMode, $replaceExisting);
    $preview['token'] = $token;
    $preview['source_file'] = (string)($state['filename'] ?? 'cards.zip');
    $_SESSION['siid_imports'][$token] = ['dir'=>$dir, 'files'=>$files, 'config'=>[
      'key_field'=>$keyField, 'image_type'=>$imageType, 'faces_mode'=>$facesMode, 'replace_existing'=>$replaceExisting, 'source_file'=>$preview['source_file']
    ]];
  } elseif ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'preview') {
    $keyField = (string)($_POST['key_field'] ?? 'national_id');
    if (!isset($keyFields[$keyField])) $keyField = 'id';
    $imageType = (string)($_POST['image_type'] ?? 'front');
    if (!in_array($imageType, ['front','back','both'], true)) $imageType = 'front';
    $facesMode = (string)($_POST['faces_mode'] ?? 'same');
    if (!in_array($facesMode, ['same','separate'], true)) $facesMode = 'same';
    $replaceExisting = !empty($_POST['replace_existing']);
    if (empty($_FILES['cards_zip']) || (int)($_FILES['cards_zip']['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) throw new RuntimeException('اختر ملف ZIP يحتوي صور البطاقات.');
    $tmp = (string)($_FILES['cards_zip']['tmp_name'] ?? '');
    $name = (string)($_FILES['cards_zip']['name'] ?? '');
    if ($tmp === '' || !is_uploaded_file($tmp)) throw new RuntimeException('ملف الرفع غير صالح.');
    if (strtolower(pathinfo($name, PATHINFO_EXTENSION)) !== 'zip') throw new RuntimeException('الملف يجب أن يكون ZIP.');
    $token = bin2hex(random_bytes(8));
    $dir = siid_tmp_root() . DIRECTORY_SEPARATOR . $token;
    if (!is_dir($dir)) @mkdir($dir, 0775, true);
    $zipForRead = $dir . DIRECTORY_SEPARATOR . 'source.zip';
    if (!@copy($tmp, $zipForRead)) throw new RuntimeException('تعذر تجهيز ملف ZIP للقراءة.');
    $files = siid_extract_zip($zipForRead, $dir);
    if (!$files) throw new RuntimeException('لم يتم العثور على صور مدعومة داخل ZIP.');
    $preview = siid_build_preview($conn, $files, $keyField, $imageType, $facesMode, $replaceExisting);
    $preview['token'] = $token;
    $preview['source_file'] = $name;
    $_SESSION['siid_imports'][$token] = ['dir'=>$dir, 'files'=>$files, 'config'=>[
      'key_field'=>$keyField, 'image_type'=>$imageType, 'faces_mode'=>$facesMode, 'replace_existing'=>$replaceExisting, 'source_file'=>$name
    ]];
  } elseif ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'commit') {
    $token = (string)($_POST['token'] ?? '');
    $state = $_SESSION['siid_imports'][$token] ?? null;
    if (!$state || empty($state['files']) || empty($state['config'])) throw new RuntimeException('انتهت جلسة المعاينة. أعد رفع الملف.');
    $cfg = $state['config'];
    $preview = siid_build_preview($conn, $state['files'], $cfg['key_field'], $cfg['image_type'], $cfg['faces_mode'] ?? 'same', (bool)$cfg['replace_existing']);
    $updated = 0; $skipped = 0;
    foreach ($preview['items'] as $item) {
      if (!in_array($item['status'], ['exact','fuzzy'], true) || empty($item['subscriber']) || !in_array($item['side'], ['front','back','both'], true)) { $skipped++; continue; }
      $sid = (int)$item['subscriber']['id'];
      [$abs, $rel] = siid_public_dest($item['file'], $sid, $item['side']);
      if (!@copy((string)$item['file']['path'], $abs)) { $skipped++; continue; }
      siid_update_card($conn, $sid, $item['side'], $rel);
      $updated++;
      if (function_exists('audit_log')) @audit_log($conn, 'SUBSCRIBER_ID_CARD_IMPORT', 'subscribers', $sid, $item['file']['original'] . ' => ' . $rel);
    }
    siid_clean_dir((string)$state['dir']);
    unset($_SESSION['siid_imports'][$token]);
    $msg = 'تم استيراد ' . $updated . ' صورة بطاقة. تم تجاوز ' . $skipped . ' ملف.';
    $preview = null;
  }
} catch (Throwable $e) {
  $err = $e->getMessage();
}

$UA_FORCE_DESIGN_V2 = true;
include __DIR__ . '/header.php';
include __DIR__ . '/sidebar.php';
?>
<style>
body.ua-popup .content-wrapper{margin:0!important;background:#0b1220!important;color:#e5e7eb;min-height:100vh}
.siid-page{padding:14px;direction:rtl;background:linear-gradient(180deg,#071827,#0b1220);min-height:100vh}
.siid-wrap{max-width:1180px;margin:0 auto}
.siid-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:12px}
.siid-title{display:flex;align-items:center;gap:10px}.siid-title i{color:#fbbf24}.siid-title h1{font-size:20px;margin:0;font-weight:900;color:#fff}.siid-title p{margin:3px 0 0;color:#94a3b8;font-size:12px}
.siid-card{border:1px solid rgba(148,163,184,.22);border-radius:10px;background:rgba(15,23,42,.88);box-shadow:none;margin-bottom:12px;overflow:hidden}
.siid-card-head{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:12px 14px;border-bottom:1px solid rgba(148,163,184,.18);background:rgba(2,6,23,.28)}
.siid-card-head h2{font-size:15px;margin:0;color:#fff;font-weight:900}.siid-card-body{padding:14px}
.siid-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px}.siid-grid-3{grid-template-columns:repeat(3,minmax(0,1fr))}.siid-field{display:grid;gap:6px}.siid-field label,.siid-field-label{font-size:12px;font-weight:800;color:#cbd5e1}
.siid-control{height:38px;border:1px solid rgba(148,163,184,.34);border-radius:8px;background:#111827;color:#fff;padding:0 10px}
.siid-file{height:auto;min-height:38px;padding:8px}.siid-check{display:flex;align-items:center;gap:8px;font-size:12px;color:#d1d5db;margin-top:22px}
.siid-actions{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:12px}.siid-btn{border:0;border-radius:8px;height:36px;padding:0 14px;font-weight:900;cursor:pointer;display:inline-flex;align-items:center;gap:7px;text-decoration:none}
.siid-btn-primary{background:#14b8a6;color:#04111a}.siid-btn-danger{background:#ef4444;color:#fff}.siid-btn-secondary{background:#334155;color:#fff}
.siid-stats{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:8px;margin-bottom:12px}.siid-stat{border:1px solid rgba(148,163,184,.22);border-radius:8px;background:#0f172a;padding:10px}.siid-stat[data-siid-filter]{cursor:pointer}.siid-stat[data-siid-filter]:hover{border-color:rgba(20,184,166,.55)}.siid-stat span{display:block;color:#94a3b8;font-size:11px}.siid-stat b{font-size:22px;color:#fff}
.siid-table-wrap{max-height:360px;overflow:auto}.siid-table{width:100%;border-collapse:collapse;color:#e5e7eb;font-size:12px}.siid-table th,.siid-table td{border-bottom:1px solid rgba(148,163,184,.18);padding:8px;text-align:right;vertical-align:middle}.siid-table th{position:sticky;top:0;background:#111827;color:#fff;z-index:1}.siid-table tr.siid-row-fuzzy td{background:rgba(234,179,8,.10)}
.siid-badge{display:inline-flex;align-items:center;border-radius:999px;padding:3px 8px;font-size:11px;font-weight:900}.is-exact{background:rgba(34,197,94,.16);color:#86efac}.is-fuzzy{background:rgba(234,179,8,.18);color:#fde68a}.is-bad{background:rgba(239,68,68,.16);color:#fecaca}.is-soft{background:rgba(148,163,184,.18);color:#cbd5e1}
.siid-alert{border-radius:8px;padding:10px 12px;margin-bottom:12px;font-weight:800}.siid-alert-ok{background:rgba(34,197,94,.16);color:#bbf7d0}.siid-alert-err{background:rgba(239,68,68,.16);color:#fecaca}.siid-alert-warn{background:rgba(234,179,8,.14);color:#fde68a;border:1px solid rgba(234,179,8,.24)}
.siid-filterbar{display:flex;align-items:center;gap:7px;flex-wrap:wrap;margin-bottom:10px}.siid-filter-btn{height:30px;border:1px solid rgba(148,163,184,.28);border-radius:8px;background:#111827;color:#dbeafe;padding:0 10px;font-size:11px;font-weight:900;cursor:pointer}.siid-filter-btn:hover,.siid-filter-btn.is-active{border-color:rgba(20,184,166,.65);background:rgba(20,184,166,.16);color:#ccfbf1}.siid-filter-btn.is-hot{border-color:rgba(234,179,8,.55);color:#fde68a}
.siid-filter-note{margin-inline-start:auto;color:#94a3b8;font-size:11px;font-weight:800}.siid-fuzzy-list{display:grid;gap:6px;margin:6px 0 0;padding:0;list-style:none}.siid-fuzzy-list li{display:flex;justify-content:space-between;gap:10px;border-top:1px solid rgba(234,179,8,.16);padding-top:6px}
.siid-help-wrap{display:inline-flex;align-items:center;position:relative;margin-inline-start:6px;vertical-align:middle}
.siid-help-btn{width:18px;height:18px;border:1px solid rgba(125,211,252,.45);border-radius:50%;background:#0ea5e9;color:#fff;font-size:11px;line-height:1;font-weight:900;display:inline-flex;align-items:center;justify-content:center;cursor:help;box-shadow:none;padding:0}
.siid-help-tip{position:absolute;top:24px;right:0;z-index:20;min-width:320px;max-width:min(520px,90vw);display:none;border:1px solid rgba(125,211,252,.30);border-radius:8px;background:#0f172a;color:#dbeafe;padding:8px 10px;font-size:12px;font-weight:800;line-height:1.7;box-shadow:0 14px 30px rgba(0,0,0,.30)}
.siid-help-wrap:hover .siid-help-tip,.siid-help-btn:focus + .siid-help-tip{display:block}
.siid-upload-status{color:#99f6e4;font-size:12px;font-weight:900}
.siid-report-grid{display:grid;grid-template-columns:2fr 1fr;gap:12px}
@media(max-width:900px){.siid-grid,.siid-stats,.siid-report-grid{grid-template-columns:1fr}.siid-head{align-items:flex-start;flex-direction:column}}
</style>
<div class="content-wrapper">
  <section class="content siid-page">
    <div class="siid-wrap">
      <div class="siid-head">
        <div class="siid-title">
          <i class="fa fa-id-card-o"></i>
          <div><h1>استيراد البطاقات الشخصية</h1><p>مطابقة أسماء الصور مع بيانات المشتركين ثم استيراد آمن بعد المعاينة.</p></div>
        </div>
      </div>
      <?php if ($msg): ?><div class="siid-alert siid-alert-ok"><?= siid_h($msg) ?></div><?php endif; ?>
      <?php if ($err): ?><div class="siid-alert siid-alert-err"><?= siid_h($err) ?></div><?php endif; ?>

      <section class="siid-card">
        <div class="siid-card-head"><h2>1) إعداد المطابقة</h2></div>
        <div class="siid-card-body">
          <form method="post" enctype="multipart/form-data" id="siidPreviewForm">
            <input type="hidden" name="action" value="preview">
            <div class="siid-grid">
              <div class="siid-field">
                <div class="siid-field-label">
                  <span>ملف الصور ZIP</span>
                  <span class="siid-help-wrap">
                    <button type="button" class="siid-help-btn" aria-label="معلومة عن حد الرفع">?</button>
                    <span class="siid-help-tip">حد الرفع الحالي: upload <?= siid_h($uploadMaxText) ?> / post <?= siid_h($postMaxText) ?>. إذا كان ملف ZIP أكبر من هذا الحد سيرفضه البرنامج قبل المعاينة.</span>
                  </span>
                </div>
                <input class="siid-control siid-file" type="file" name="cards_zip" accept=".zip,application/zip" required>
              </div>
              <div class="siid-field">
                <label>مفتاح الربط بين اسم الصورة والبرنامج</label>
                <select class="siid-control" name="key_field">
                  <?php foreach ($keyFields as $k=>$label): ?><option value="<?= siid_h($k) ?>" <?= $k==='national_id'?'selected':'' ?>><?= siid_h($label) ?></option><?php endforeach; ?>
                </select>
              </div>
              <div class="siid-field">
                <label>نوع الصورة المحملة</label>
                <select class="siid-control" name="image_type">
                  <option value="front">الوجه الأمامي فقط</option>
                  <option value="back">الوجه الخلفي فقط</option>
                  <option value="both">الوجهين</option>
                </select>
              </div>
              <div class="siid-field" id="siidFacesModeWrap">
                <label>طريقة وجود الوجهين</label>
                <select class="siid-control" name="faces_mode">
                  <option value="same">الوجهين ضمن نفس الصورة</option>
                  <option value="separate">كل وجه بصورة مستقلة</option>
                </select>
              </div>
            </div>
            <label class="siid-check"><input type="checkbox" name="replace_existing" value="1"> استبدال الصور الموجودة مسبقاً للمشتركين المطابقين</label>
            <div class="siid-actions">
              <button class="siid-btn siid-btn-primary" type="submit" id="siidPreviewBtn"><i class="fa fa-search"></i> بدء الجرد والمعاينة</button>
              <span id="siidUploadStatus" class="siid-upload-status"></span>
            </div>
          </form>
        </div>
      </section>

      <?php if ($preview): ?>
        <?php
          $fuzzyItems = [];
          foreach ($preview['items'] as $candidate) {
            if (($candidate['status'] ?? '') === 'fuzzy') $fuzzyItems[] = $candidate;
          }
        ?>
        <section class="siid-stats" aria-label="ملخص المطابقة">
          <div class="siid-stat" data-siid-filter="all"><span>كل الملفات</span><b><?= (int)$preview['counts']['total'] ?></b></div>
          <div class="siid-stat" data-siid-filter="ready"><span>جاهزة للاستيراد</span><b><?= (int)$preview['counts']['ready'] ?></b></div>
          <div class="siid-stat" data-siid-filter="exact"><span>تطابق 100%</span><b><?= (int)$preview['counts']['exact'] ?></b></div>
          <div class="siid-stat" data-siid-filter="fuzzy"><span>تطابق 90%</span><b><?= (int)$preview['counts']['fuzzy'] ?></b></div>
          <div class="siid-stat" data-siid-filter="unmatched"><span>غير مطابقة</span><b><?= (int)$preview['counts']['unmatched'] ?></b></div>
          <div class="siid-stat" data-siid-filter="side_unknown"><span>نوع غير واضح</span><b><?= (int)$preview['counts']['side_unknown'] ?></b></div>
          <div class="siid-stat"><span>بدون صور بملفهم</span><b><?= count($preview['missing']) ?></b></div>
        </section>
        <?php if (!empty($fuzzyItems)): ?>
          <div class="siid-alert siid-alert-warn">
            <div>مطابقات 90% تحتاج مراجعة قبل الاستيراد:</div>
            <ul class="siid-fuzzy-list">
              <?php foreach (array_slice($fuzzyItems, 0, 8) as $fit):
                $fsub = $fit['subscriber'] ?? [];
              ?>
                <li>
                  <span><?= siid_h($fit['file']['original'] ?? '') ?> ← <?= $fsub ? ('#'.(int)$fsub['id'].' - '.siid_h($fsub['full_name'] ?? $fsub['username'] ?? '')) : '—' ?></span>
                  <span><?= siid_h($fit['key'] ?? '') ?></span>
                </li>
              <?php endforeach; ?>
            </ul>
          </div>
        <?php endif; ?>
        <?php if (!empty($preview['counts']['side_unknown'])): ?>
          <div class="siid-alert siid-alert-warn">تمت مطابقة أسماء الصور مع المشتركين، لكن نوع الصورة غير واضح. إذا كان كل وجه بصورة مستقلة يجب أن يحتوي اسم الملف على front/back أو أمامي/خلفي. أما إذا كانت الصورة الواحدة تحتوي الوجهين فاختر "الوجهين ضمن نفس الصورة".</div>
        <?php endif; ?>
        <section class="siid-card">
          <div class="siid-card-head">
            <h2>2) نتيجة المطابقة</h2>
            <form method="post" class="js-siid-commit">
              <input type="hidden" name="action" value="commit">
              <input type="hidden" name="token" value="<?= siid_h($preview['token']) ?>">
              <button class="siid-btn siid-btn-primary" type="submit" <?= (int)$preview['counts']['ready'] <= 0 ? 'disabled' : '' ?>><i class="fa fa-save"></i> تنفيذ الاستيراد</button>
            </form>
          </div>
          <div class="siid-card-body">
            <div class="siid-filterbar" aria-label="تصفية نتائج المطابقة">
              <button type="button" class="siid-filter-btn is-active" data-siid-filter="all">الكل <?= (int)$preview['counts']['total'] ?></button>
              <button type="button" class="siid-filter-btn" data-siid-filter="ready">جاهزة <?= (int)$preview['counts']['ready'] ?></button>
              <button type="button" class="siid-filter-btn" data-siid-filter="exact">100% <?= (int)$preview['counts']['exact'] ?></button>
              <button type="button" class="siid-filter-btn is-hot" data-siid-filter="fuzzy">90% <?= (int)$preview['counts']['fuzzy'] ?></button>
              <button type="button" class="siid-filter-btn" data-siid-filter="unmatched">غير مطابقة <?= (int)$preview['counts']['unmatched'] ?></button>
              <button type="button" class="siid-filter-btn" data-siid-filter="side_unknown">نوع غير واضح <?= (int)$preview['counts']['side_unknown'] ?></button>
              <button type="button" class="siid-filter-btn" data-siid-filter="has_existing">موجود مسبقاً <?= (int)$preview['counts']['has_existing'] ?></button>
              <span class="siid-filter-note" id="siidFilterNote"></span>
            </div>
            <div class="siid-table-wrap">
              <table class="siid-table" id="siidResultsTable">
                <thead><tr><th>الملف</th><th>المفتاح المقروء</th><th>النوع</th><th>المشترك</th><th>درجة التطابق</th><th>النتيجة</th></tr></thead>
                <tbody>
              <?php foreach (array_slice($preview['items'], 0, 400) as $it):
                $sub = $it['subscriber'] ?? [];
                $status = (string)$it['status'];
                $cls = in_array($status, ['exact','fuzzy'], true) ? ($status === 'exact' ? 'is-exact' : 'is-fuzzy') : (in_array($status, ['has_existing'], true) ? 'is-soft' : 'is-bad');
                $label = ['exact'=>'مطابق 100%','fuzzy'=>'مطابق 90%','unmatched'=>'غير موجود','ambiguous'=>'تطابق متعدد','side_unknown'=>'نوع الصورة غير واضح','has_existing'=>'لديه صورة مسبقاً'][$status] ?? $status;
              ?>
                <tr data-status="<?= siid_h($status) ?>" class="<?= $status === 'fuzzy' ? 'siid-row-fuzzy' : '' ?>">
                  <td><?= siid_h($it['file']['original']) ?></td>
                  <td><?= siid_h($it['key']) ?></td>
                  <td><?= siid_h(siid_side_label($it['side'])) ?></td>
                  <td><?= $sub ? ('#'.(int)$sub['id'].' - '.siid_h($sub['full_name'] ?? $sub['username'] ?? '')) : '—' ?></td>
                  <td><?= !empty($it['score']) ? ((int)$it['score'] . '%') : '—' ?></td>
                  <td><span class="siid-badge <?= $cls ?>"><?= siid_h($label) ?></span></td>
                </tr>
              <?php endforeach; ?>
                </tbody>
              </table>
            </div>
          </div>
        </section>
        <section class="siid-report-grid">
          <div class="siid-card">
            <div class="siid-card-head"><h2>مشتركون لا توجد لهم صور بطاقة بملفهم</h2></div>
            <div class="siid-card-body siid-table-wrap">
              <table class="siid-table"><tbody>
              <?php foreach (array_slice($preview['missing'], 0, 200) as $m): ?>
                <tr><td>#<?= (int)$m['id'] ?></td><td><?= siid_h($m['full_name'] ?? $m['username'] ?? '') ?></td><td><?= !empty($m['missing_front']) ? 'الأمامي ' : '' ?><?= !empty($m['missing_back']) ? 'الخلفي' : '' ?></td></tr>
              <?php endforeach; ?>
              </tbody></table>
            </div>
          </div>
          <div class="siid-card">
            <div class="siid-card-head"><h2>تنبيهات</h2></div>
            <div class="siid-card-body">
              <p>في وضع "كل وجه بصورة مستقلة"، يجب أن يحتوي اسم الملف على front/back أو أمامي/خلفي حتى يعرف البرنامج مكان حفظ كل صورة.</p>
              <p>في وضع "الوجهين ضمن نفس الصورة"، سيتم حفظ الصورة نفسها في خانة الوجه الأمامي وخانة الوجه الخلفي للمشترك المطابق.</p>
              <p>الاستيراد يتجاوز الملفات غير المطابقة أو المتعددة أو غير واضحة النوع.</p>
              <p>بدون خيار الاستبدال، أي مشترك لديه صورة مسبقاً لن يتم تعديلها.</p>
            </div>
          </div>
        </section>
      <?php endif; ?>
    </div>
  </section>
</div>
<script>
(function(){
  window.__siidSubmitting = false;
  const previewForm = document.getElementById('siidPreviewForm');
  const previewBtn = document.getElementById('siidPreviewBtn');
  const uploadStatus = document.getElementById('siidUploadStatus');
  const imageType = previewForm && previewForm.elements.image_type ? previewForm.elements.image_type : null;
  const facesModeWrap = document.getElementById('siidFacesModeWrap');
  function setStatus(text){
    if(uploadStatus) uploadStatus.textContent = text || '';
  }
  function refreshFacesMode(){
    if(!facesModeWrap || !imageType) return;
    facesModeWrap.style.display = imageType.value === 'both' ? '' : 'none';
  }
  if(imageType){
    imageType.addEventListener('change', refreshFacesMode);
    refreshFacesMode();
  }
  const resultRows = Array.from(document.querySelectorAll('#siidResultsTable tbody tr[data-status]'));
  const filterTriggers = Array.from(document.querySelectorAll('[data-siid-filter]'));
  const filterNote = document.getElementById('siidFilterNote');
  const tableWrap = document.querySelector('#siidResultsTable') ? document.querySelector('#siidResultsTable').closest('.siid-table-wrap') : null;
  function rowMatchesFilter(row, filter){
    const status = row.getAttribute('data-status') || '';
    if(filter === 'all') return true;
    if(filter === 'ready') return status === 'exact' || status === 'fuzzy';
    return status === filter;
  }
  function applyResultsFilter(filter){
    let visible = 0;
    resultRows.forEach(function(row){
      const show = rowMatchesFilter(row, filter);
      row.style.display = show ? '' : 'none';
      if(show) visible++;
    });
    filterTriggers.forEach(function(btn){
      btn.classList.toggle('is-active', (btn.getAttribute('data-siid-filter') || '') === filter);
    });
    if(filterNote) filterNote.textContent = 'المعروض: ' + visible;
    if(tableWrap) tableWrap.scrollTop = 0;
  }
  filterTriggers.forEach(function(btn){
    btn.addEventListener('click', function(){
      applyResultsFilter(btn.getAttribute('data-siid-filter') || 'all');
    });
  });
  if(resultRows.length) applyResultsFilter('all');
  async function postChunkForm(fd){
    const res = await fetch('subscribers_import_id_cards.php?embed=1', {method:'POST', body:fd, credentials:'same-origin', headers:{'X-Requested-With':'XMLHttpRequest'}});
    const data = await res.json().catch(()=>({ok:false,message:'استجابة غير مفهومة من الخادم'}));
    if(!res.ok || !data.ok) throw new Error(data.message || 'فشل رفع الملف.');
    return data;
  }
  function submitChunkPreview(form, token){
    const submit = document.createElement('form');
    submit.method = 'post';
    submit.action = 'subscribers_import_id_cards.php?embed=1';
    const add = (name, value)=>{ const input = document.createElement('input'); input.type = 'hidden'; input.name = name; input.value = value; submit.appendChild(input); };
    add('action', 'preview_chunked');
    add('upload_token', token);
    add('key_field', form.elements.key_field ? form.elements.key_field.value : 'id');
    add('image_type', form.elements.image_type ? form.elements.image_type.value : 'front');
    add('faces_mode', form.elements.faces_mode ? form.elements.faces_mode.value : 'same');
    if(form.elements.replace_existing && form.elements.replace_existing.checked) add('replace_existing', '1');
    document.body.appendChild(submit);
    window.__siidSubmitting = true;
    submit.submit();
  }
  if(previewForm){
    previewForm.addEventListener('submit', async function(e){
      const fileInput = previewForm.querySelector('input[name="cards_zip"]');
      const file = fileInput && fileInput.files ? fileInput.files[0] : null;
      if(!file || file.size <= 6 * 1024 * 1024 || previewForm.dataset.chunkBypass === '1') return;
      e.preventDefault();
      e.stopImmediatePropagation();
      try{
        if(previewBtn) previewBtn.disabled = true;
        const chunkSize = 1 * 1024 * 1024;
        const total = Math.ceil(file.size / chunkSize);
        setStatus('تجهيز رفع الملف...');
        const initFd = new FormData();
        initFd.append('action', 'chunk_init');
        initFd.append('filename', file.name);
        initFd.append('total_chunks', String(total));
        const init = await postChunkForm(initFd);
        for(let i=0; i<total; i++){
          const start = i * chunkSize;
          const end = Math.min(file.size, start + chunkSize);
          const fd = new FormData();
          fd.append('action', 'chunk_upload');
          fd.append('token', init.token);
          fd.append('index', String(i));
          fd.append('chunk', file.slice(start, end), file.name + '.part' + i);
          setStatus('رفع الملف: ' + Math.round((i / total) * 100) + '%');
          await postChunkForm(fd);
        }
        setStatus('اكتمل الرفع، جاري الجرد والمعاينة...');
        submitChunkPreview(previewForm, init.token);
      }catch(err){
        setStatus(err && err.message ? err.message : 'فشل رفع الملف.');
        if(previewBtn) previewBtn.disabled = false;
      }
    }, true);
  }
  document.addEventListener('submit', function(e){
    if(e.target && e.target.classList.contains('js-siid-commit')){
      if(!confirm('سيتم حفظ الصور المطابقة داخل ملفات المشتركين. هل تريد المتابعة؟')){
        e.preventDefault();
        return;
      }
    }
    window.__siidSubmitting = true;
  }, true);
  window.addEventListener('beforeunload', function(){
    if(window.__siidSubmitting) return;
    try { navigator.sendBeacon('subscribers_import_id_cards.php?action=clear_import_silent', new Blob(['1'], {type:'text/plain'})); } catch(e) {}
  });
})();
</script>
<?php include __DIR__ . '/footer.php'; ?>
