<?php
declare(strict_types=1);

mysqli_report(MYSQLI_REPORT_OFF);
require_once __DIR__ . '/../db_bootstrap.php';
require_once __DIR__ . '/../billing_cycle_lib.php';
require_once __DIR__ . '/../finance_payment_lib.php';
require_once __DIR__ . '/../subscriber_service_pause_lib.php';

$conn = ensure_db(true);
if (!($conn instanceof mysqli)) {
  fwrite(STDERR, "FAIL db_not_ready\n");
  exit(1);
}
billing_cycle_migrate($conn);
finance_ensure_payments_table($conn);

$testKey = 'BILLTEST-' . date('YmdHis') . '-' . bin2hex(random_bytes(3));
$subscriberId = 0;
$invoiceIds = [];
$paymentIds = [];
$branchRef = $testKey;
$failures = [];

function billing_test_assert(bool $condition, string $label, array &$failures): void {
  echo ($condition ? 'PASS ' : 'FAIL ') . $label . PHP_EOL;
  if (!$condition) $failures[] = $label;
}

try {
  $speedRs = $conn->query("SELECT speed_name,price FROM speeds WHERE price>0 ORDER BY id ASC LIMIT 1");
  $speed = $speedRs ? $speedRs->fetch_assoc() : null;
  if (!$speed) throw new RuntimeException('لا توجد سرعة ذات سعر صالح للاختبار.');
  $speedName = (string)$speed['speed_name'];
  $price = round((float)$speed['price'], 2);

  $stmt = $conn->prepare("INSERT INTO subscribers
    (username,full_name,phone,speed,speed_name,package,package_name,status,is_deleted,ppp_username,ppp_password,billing_paused,billing_state,created_at,updated_at)
    VALUES (?,?,?,?,?,'مفتوحة','مفتوحة','نشط',0,'','',0,'CURRENT',NOW(),NOW())");
  if (!$stmt) throw new RuntimeException($conn->error);
  $username = strtolower($testKey);
  $fullName = 'مشترك اختبار دورة الفوترة';
  $phone = '0000000000';
  $stmt->bind_param('sssss', $username, $fullName, $phone, $speedName, $speedName);
  if (!$stmt->execute()) throw new RuntimeException($stmt->error);
  $subscriberId = (int)$stmt->insert_id;
  $stmt->close();

  $advance = finance_add_subscriber_credit($conn, $subscriberId, $price * 2, [
    'method'=>'اختبار',
    'reference'=>$testKey . '-ADV',
    'note'=>'اختبار الرصيد المقدم',
    'source_type'=>'main',
    'source_ref'=>'main',
    'source_name'=>'النسخة الرئيسية',
    'channel'=>'test',
    'idempotency_key'=>$testKey . '-ADVANCE',
  ]);
  billing_test_assert(!empty($advance['ok']), 'تسجيل الدفعة المقدمة', $failures);
  if (empty($advance['ok'])) throw new RuntimeException('advance: ' . (string)($advance['error'] ?? 'unknown'));
  if (!empty($advance['payment_id'])) $paymentIds[] = (int)$advance['payment_id'];

  $cycles = [
    ['label'=>$testKey.'-1','start_date'=>'2097-01-01','end_date'=>'2097-01-31','due_date'=>'2097-01-01','month_key'=>'2097-01'],
    ['label'=>$testKey.'-2','start_date'=>'2097-02-01','end_date'=>'2097-02-28','due_date'=>'2097-02-01','month_key'=>'2097-02'],
    ['label'=>$testKey.'-3','start_date'=>'2097-03-01','end_date'=>'2097-03-31','due_date'=>'2097-03-01','month_key'=>'2097-03'],
  ];
  foreach ($cycles as $index => $cycle) {
    $generated = billing_generate_cycle_invoices($conn, $cycle, [$subscriberId]);
    billing_test_assert((int)($generated['created'] ?? 0) === 1, 'إنشاء فاتورة الدورة ' . ($index + 1), $failures);
    if ((int)($generated['created'] ?? 0) !== 1) throw new RuntimeException('generation: ' . (string)($generated['error'] ?? json_encode($generated, JSON_UNESCAPED_UNICODE)));
    $keyEsc = $conn->real_escape_string('BASE:' . $subscriberId . ':' . $cycle['start_date']);
    $invoiceRs = $conn->query("SELECT * FROM invoices WHERE generation_key='{$keyEsc}' LIMIT 1");
    $invoice = $invoiceRs ? $invoiceRs->fetch_assoc() : null;
    if (!$invoice) throw new RuntimeException('تعذر قراءة فاتورة الاختبار.');
    $invoiceIds[] = (int)$invoice['id'];
    if ($index < 2) {
      billing_test_assert(strtolower((string)$invoice['payment_status']) === 'paid', 'تغطية الفاتورة ' . ($index + 1) . ' آلياً من الرصيد', $failures);
    } else {
      billing_test_assert((float)$invoice['remaining_amount'] === $price, 'بقاء الفاتورة الثالثة مستحقة بعد نفاد الرصيد', $failures);
    }
  }

  billing_test_assert(abs(billing_credit_balance($conn, $subscriberId)) < 0.001, 'نفاد الرصيد بعد تغطية فاتورتين', $failures);
  $thirdInvoiceId = $invoiceIds[2];
  $earlyPayment = finance_pay_invoice($conn, $thirdInvoiceId, [
    'amount'=>$price,
    'method'=>'اختبار',
    'reference'=>$testKey . '-EARLY-PAY',
    'idempotency_key'=>$testKey . '-EARLY-PAY',
  ]);
  billing_test_assert(empty($earlyPayment['ok']), 'منع التسديد قبل يوم الاستحقاق', $failures);
  $pendingRs = $conn->query("SELECT COUNT(*) c FROM billing_action_requests WHERE invoice_id={$thirdInvoiceId} AND action_type='REMINDER' AND status='pending'");
  $pendingCount = $pendingRs ? (int)($pendingRs->fetch_assoc()['c'] ?? 0) : 0;
  billing_test_assert($pendingCount === 0, 'عدم إنشاء طلب رسالة قبل يوم الاستحقاق', $failures);
  $conn->query("UPDATE invoices SET due_date=CURDATE() WHERE id={$thirdInvoiceId} LIMIT 1");
  billing_queue_requests($conn);
  $pendingRs = $conn->query("SELECT COUNT(*) c FROM billing_action_requests WHERE invoice_id={$thirdInvoiceId} AND action_type='REMINDER' AND status='pending'");
  $pendingCount = $pendingRs ? (int)($pendingRs->fetch_assoc()['c'] ?? 0) : 0;
  billing_test_assert($pendingCount === 1, 'إنشاء طلب موافقة لرسالة التسديد في يوم الاستحقاق دون إرسال مباشر', $failures);

  $outboxBefore = 0;
  if (table_exists($conn, 'message_outbox')) {
    $outboxRs = $conn->query("SELECT COUNT(*) c FROM message_outbox WHERE subscriber_id={$subscriberId}");
    $outboxBefore = $outboxRs ? (int)($outboxRs->fetch_assoc()['c'] ?? 0) : 0;
  }
  billing_test_assert($outboxBefore === 0, 'عدم إرسال الرسالة قبل موافقة السوبر أدمن', $failures);

  $requestRs = $conn->query("SELECT id FROM billing_action_requests WHERE invoice_id={$thirdInvoiceId} AND action_type='REMINDER' AND status='pending' LIMIT 1");
  $requestId = $requestRs ? (int)($requestRs->fetch_assoc()['id'] ?? 0) : 0;
  $approved = billing_approve_requests($conn, 'REMINDER', [$requestId], 1);
  billing_test_assert((int)($approved['success'] ?? 0) === 1, 'اعتماد رسالة التسديد جماعياً عبر خدمة الموافقات ' . json_encode($approved, JSON_UNESCAPED_UNICODE), $failures);

  @$conn->query("UPDATE subscribers SET ppp_is_disabled=1,mt_disabled=1,speed_state='CUT' WHERE id={$subscriberId}");
  $pause = subscriber_set_temporary_pause($conn, $subscriberId, true, 1, 'اختبار الإيقاف');
  billing_test_assert(!empty($pause['ok']), 'إيقاف المشترك وحسابه ودورة الفوترة مع حفظ حالته السابقة', $failures);
  $pausedCycle = ['label'=>$testKey.'-4','start_date'=>'2097-04-01','end_date'=>'2097-04-30','due_date'=>'2097-04-01','month_key'=>'2097-04'];
  $pausedGeneration = billing_generate_cycle_invoices($conn, $pausedCycle, [$subscriberId]);
  billing_test_assert((int)($pausedGeneration['created'] ?? 0) === 0, 'منع تراكم فاتورة جديدة أثناء الإيقاف المؤقت', $failures);

  $payment = finance_pay_invoice($conn, $thirdInvoiceId, [
    'amount'=>$price,
    'method'=>'اختبار',
    'reference'=>$testKey . '-BRANCH-PAY',
    'note'=>'اختبار دفع من نسخة فرعية',
    'source_type'=>'branch',
    'source_ref'=>$branchRef,
    'source_name'=>'نسخة فرعية اختبارية',
    'source_instance_id'=>$branchRef,
    'channel'=>'test',
    'idempotency_key'=>$testKey . '-PAY',
  ]);
  billing_test_assert(!empty($payment['ok']) && !empty($payment['fully_paid']), 'تسديد الفاتورة من النسخة الفرعية', $failures);
  if (!empty($payment['payment_id'])) $paymentIds[] = (int)$payment['payment_id'];
  $pausedStateRs = $conn->query("SELECT billing_paused,ppp_is_disabled,mt_disabled,speed_state FROM subscribers WHERE id={$subscriberId} LIMIT 1");
  $pausedState = $pausedStateRs ? $pausedStateRs->fetch_assoc() : [];
  billing_test_assert(
    (int)($pausedState['billing_paused'] ?? 0) === 1
      && (int)($pausedState['ppp_is_disabled'] ?? 0) === 1
      && (int)($pausedState['mt_disabled'] ?? 0) === 1
      && strtoupper((string)($pausedState['speed_state'] ?? '')) === 'CUT',
    'التسديد يحفظ الإيقاف المؤقت ولا يعيد حساب الشبكة تلقائياً',
    $failures
  );
  $resume = subscriber_set_temporary_pause($conn, $subscriberId, false, 1, '');
  $resumedStateRs = $conn->query("SELECT billing_paused,ppp_is_disabled,mt_disabled,speed_state FROM subscribers WHERE id={$subscriberId} LIMIT 1");
  $resumedState = $resumedStateRs ? $resumedStateRs->fetch_assoc() : [];
  billing_test_assert(
    !empty($resume['ok'])
      && (int)($resumedState['billing_paused'] ?? 1) === 0
      && (int)($resumedState['ppp_is_disabled'] ?? 0) === 1
      && (int)($resumedState['mt_disabled'] ?? 0) === 1
      && strtoupper((string)($resumedState['speed_state'] ?? '')) === 'CUT',
    'إعادة التفعيل تستأنف الفوترة وتحفظ القطع السابق بدلاً من تشغيل الحساب خطأً',
    $failures
  );

  $mainEntry = $conn->query("SELECT COUNT(*) c FROM collection_ledger_entries WHERE payment_id=" . (int)($payment['payment_id'] ?? 0) . " AND account_key='MAIN' AND status='POSTED'");
  $branchKeyEsc = $conn->real_escape_string('BRANCH:' . $branchRef);
  $branchEntry = $conn->query("SELECT COUNT(*) c FROM collection_ledger_entries WHERE payment_id=" . (int)($payment['payment_id'] ?? 0) . " AND account_key='{$branchKeyEsc}' AND status='POSTED'");
  billing_test_assert($mainEntry && (int)($mainEntry->fetch_assoc()['c'] ?? 0) === 1, 'ترحيل الدفعة إلى الرصيد الرئيسي', $failures);
  billing_test_assert($branchEntry && (int)($branchEntry->fetch_assoc()['c'] ?? 0) === 1, 'ترحيل الدفعة إلى رصيد النسخة الفرعية', $failures);

  $invoiceRs = $conn->query("SELECT payment_status,remaining_amount FROM invoices WHERE id={$thirdInvoiceId}");
  $paidInvoice = $invoiceRs ? $invoiceRs->fetch_assoc() : [];
  billing_test_assert(strtolower((string)($paidInvoice['payment_status'] ?? '')) === 'paid' && (float)($paidInvoice['remaining_amount'] ?? -1) === 0.0, 'تثبيت الفاتورة المدفوعة في سجلها', $failures);
} catch (Throwable $e) {
  $failures[] = $e->getMessage();
  echo 'FAIL exception: ' . $e->getMessage() . PHP_EOL;
} finally {
  if ($subscriberId > 0) {
    $posted = @$conn->query("SELECT id FROM payments WHERE subscriber_id={$subscriberId} AND payment_status='POSTED' ORDER BY id DESC");
    if ($posted) while ($row = $posted->fetch_assoc()) finance_delete_payment_row($conn, (int)$row['id']);
    if (table_exists($conn, 'message_outbox')) @$conn->query("DELETE FROM message_outbox WHERE subscriber_id={$subscriberId}");
    if (table_exists($conn, 'message_logs')) @$conn->query("DELETE FROM message_logs WHERE subscriber_id={$subscriberId}");
    @$conn->query("DELETE FROM billing_action_requests WHERE subscriber_id={$subscriberId}");
    @$conn->query("DELETE FROM subscriber_credit_ledger WHERE subscriber_id={$subscriberId}");
    @$conn->query("DELETE FROM subscriber_credit_accounts WHERE subscriber_id={$subscriberId}");
    @$conn->query("DELETE FROM payments WHERE subscriber_id={$subscriberId}");
    @$conn->query("DELETE FROM invoices WHERE subscriber_id={$subscriberId}");
    @$conn->query("DELETE FROM subscribers WHERE id={$subscriberId}");
  }
  $branchKeyEsc = $conn->real_escape_string('BRANCH:' . $branchRef);
  @$conn->query("DELETE FROM collection_ledger_entries WHERE account_key='{$branchKeyEsc}'");
  @$conn->query("DELETE FROM collection_accounts WHERE account_key='{$branchKeyEsc}' AND ABS(balance)<0.001");
}

if ($failures) {
  fwrite(STDERR, 'FAILED ' . count($failures) . ': ' . implode(' | ', $failures) . PHP_EOL);
  exit(1);
}
echo "ALL BILLING POLICY TESTS PASSED\n";
