<?php
/**
 * Temporary subscriber suspension service.
 *
 * A temporary suspension is one business state with two coordinated effects:
 * the PPP account is stopped and the billing cycle is paused. Existing
 * invoices, outstanding balances, and advance credit are never modified.
 */

require_once __DIR__ . '/billing_account_lib.php';
require_once __DIR__ . '/mikrotik_lib.php';
require_once __DIR__ . '/subscriber_history_lib.php';
require_once __DIR__ . '/subscribers_bulk_action_lib.php';

if (!function_exists('subscriber_service_pause_snapshot')) {
  function subscriber_service_pause_snapshot(array $subscriber): array {
    $pppDisabled = (int)($subscriber['ppp_is_disabled'] ?? 0) === 1;
    $mtDisabled = (int)($subscriber['mt_disabled'] ?? 0) === 1;
    $speedState = strtoupper(trim((string)($subscriber['speed_state'] ?? 'ORIGINAL')));
    if ($speedState === '') $speedState = 'ORIGINAL';
    return [
      'ppp_disabled' => $pppDisabled,
      'mt_disabled' => $mtDisabled,
      'speed_state' => $speedState,
      'network_disabled' => $pppDisabled || $mtDisabled || $speedState === 'CUT',
    ];
  }
}

if (!function_exists('subscriber_service_pause_fetch')) {
  function subscriber_service_pause_fetch(mysqli $conn, int $subscriberId): ?array {
    billing_accounts_migrate($conn);
    $subscriberId = (int)$subscriberId;
    if ($subscriberId <= 0) return null;
    $rs = @$conn->query("SELECT * FROM subscribers
      WHERE id={$subscriberId} AND COALESCE(is_deleted,0)=0 LIMIT 1");
    return ($rs && ($row = $rs->fetch_assoc())) ? $row : null;
  }
}

if (!function_exists('subscriber_service_pause_restore_snapshot')) {
  function subscriber_service_pause_restore_snapshot(mysqli $conn, int $subscriberId, array $snapshot, bool $clearSavedState = false): bool {
    $subscriberId = (int)$subscriberId;
    if ($subscriberId <= 0) return false;
    $sets = [];
    if (col_exists($conn, 'subscribers', 'ppp_is_disabled')) {
      $sets[] = 'ppp_is_disabled=' . (!empty($snapshot['ppp_disabled']) ? '1' : '0');
    }
    if (col_exists($conn, 'subscribers', 'mt_disabled')) {
      $sets[] = 'mt_disabled=' . (!empty($snapshot['mt_disabled']) ? '1' : '0');
    }
    if (col_exists($conn, 'subscribers', 'speed_state')) {
      $state = $conn->real_escape_string(substr((string)($snapshot['speed_state'] ?? 'ORIGINAL'), 0, 32));
      $sets[] = "speed_state='{$state}'";
    }
    if ($clearSavedState) {
      $sets[] = 'service_pause_prev_ppp_disabled=NULL';
      $sets[] = 'service_pause_prev_mt_disabled=NULL';
      $sets[] = 'service_pause_prev_speed_state=NULL';
    }
    return !$sets || (bool)@$conn->query('UPDATE subscribers SET ' . implode(',', $sets) . ' WHERE id=' . $subscriberId . ' LIMIT 1');
  }
}

if (!function_exists('subscriber_set_temporary_pause')) {
  function subscriber_set_temporary_pause(
    mysqli $conn,
    int $subscriberId,
    bool $paused,
    int $actorId = 0,
    string $reason = ''
  ): array {
    billing_accounts_migrate($conn);
    $subscriber = subscriber_service_pause_fetch($conn, $subscriberId);
    if (!$subscriber) return ['ok'=>false, 'error'=>'المشترك غير موجود.'];

    $currentlyPaused = (int)($subscriber['billing_paused'] ?? 0) === 1;
    if ($currentlyPaused === $paused) {
      return ['ok'=>true, 'paused'=>$paused, 'unchanged'=>true];
    }

    $currentSnapshot = subscriber_service_pause_snapshot($subscriber);
    $networkChanged = false;
    $networkAction = '';

    if ($paused) {
      $savedSnapshot = $currentSnapshot;
    } else {
      $savedSnapshot = [
        'ppp_disabled' => $subscriber['service_pause_prev_ppp_disabled'] !== null
          ? (int)$subscriber['service_pause_prev_ppp_disabled'] === 1
          : $currentSnapshot['ppp_disabled'],
        'mt_disabled' => $subscriber['service_pause_prev_mt_disabled'] !== null
          ? (int)$subscriber['service_pause_prev_mt_disabled'] === 1
          : $currentSnapshot['mt_disabled'],
        'speed_state' => $subscriber['service_pause_prev_speed_state'] !== null
          ? (string)$subscriber['service_pause_prev_speed_state']
          : (string)$currentSnapshot['speed_state'],
      ];
      $savedSnapshot['network_disabled'] = !empty($savedSnapshot['ppp_disabled'])
        || !empty($savedSnapshot['mt_disabled'])
        || strtoupper((string)$savedSnapshot['speed_state']) === 'CUT';
    }

    try {
      $conn->begin_transaction();

      if ($paused && empty($currentSnapshot['network_disabled'])) {
        $networkAction = 'cut';
        $networkResult = bulk_execute_actions($conn, 'cut', [$subscriberId], [
          'source' => 'service_pause',
          'router_context' => 'الإيقاف المؤقت للمشترك',
          'max_ids' => 1,
          'max_errors' => 1,
          'refresh_cache' => true,
          'message_prefix' => '',
        ]);
        if ((int)($networkResult['changed'] ?? 0) !== 1 || (int)($networkResult['failed'] ?? 0) !== 0) {
          throw new RuntimeException((string)($networkResult['message'] ?? 'تعذر إيقاف حساب الشبكة.'));
        }
        $networkChanged = true;
      }

      if (!$paused && empty($savedSnapshot['network_disabled'])) {
        $networkAction = 'restore';
        $networkResult = bulk_execute_actions($conn, 'restore', [$subscriberId], [
          'source' => 'service_resume',
          'router_context' => 'إعادة تفعيل المشترك',
          'max_ids' => 1,
          'max_errors' => 1,
          'refresh_cache' => true,
          'message_prefix' => '',
        ]);
        if ((int)($networkResult['changed'] ?? 0) !== 1 || (int)($networkResult['failed'] ?? 0) !== 0) {
          throw new RuntimeException((string)($networkResult['message'] ?? 'تعذر إعادة تفعيل حساب الشبكة.'));
        }
        $networkChanged = true;
      }

      if ($paused) {
        $prevSpeed = $conn->real_escape_string(substr((string)$savedSnapshot['speed_state'], 0, 32));
        $prevPpp = !empty($savedSnapshot['ppp_disabled']) ? 1 : 0;
        $prevMt = !empty($savedSnapshot['mt_disabled']) ? 1 : 0;
        if (!@$conn->query("UPDATE subscribers SET
          service_pause_prev_ppp_disabled={$prevPpp},
          service_pause_prev_mt_disabled={$prevMt},
          service_pause_prev_speed_state='{$prevSpeed}'
          WHERE id={$subscriberId} LIMIT 1")) {
          throw new RuntimeException($conn->error);
        }
      }

      $billingResult = billing_set_subscriber_pause($conn, $subscriberId, $paused, $actorId, $reason);
      if (empty($billingResult['ok'])) {
        throw new RuntimeException((string)($billingResult['error'] ?? 'تعذر تعديل حالة دورة الفوترة.'));
      }

      if (!$paused && !subscriber_service_pause_restore_snapshot($conn, $subscriberId, $savedSnapshot, true)) {
        throw new RuntimeException($conn->error ?: 'تعذر استعادة حالة حساب المشترك.');
      }

      if (function_exists('audit_log')) {
        @audit_log(
          $conn,
          $paused ? 'SUBSCRIBER_TEMPORARY_PAUSE' : 'SUBSCRIBER_TEMPORARY_RESUME',
          'subscribers',
          $subscriberId,
          ($paused ? 'paused=1' : 'paused=0') . '; balances_and_invoices_preserved=1'
        );
      }

      $conn->commit();
      return [
        'ok'=>true,
        'paused'=>$paused,
        'network_changed'=>$networkChanged,
        'previous_network_disabled'=>!empty($savedSnapshot['network_disabled']),
      ];
    } catch (Throwable $e) {
      @$conn->rollback();

      // RouterOS is external to the SQL transaction. Compensate only when this
      // operation changed it, then restore the exact pre-operation DB snapshot.
      if ($networkChanged) {
        try {
          $rollbackAction = $networkAction === 'cut' ? 'restore' : 'cut';
          bulk_execute_actions($conn, $rollbackAction, [$subscriberId], [
            'source' => 'service_pause_rollback',
            'router_context' => 'استرجاع حالة المشترك بعد تعذر الإيقاف المؤقت',
            'max_ids' => 1,
            'max_errors' => 1,
            'refresh_cache' => true,
            'message_prefix' => '',
          ]);
          subscriber_service_pause_restore_snapshot($conn, $subscriberId, $currentSnapshot, false);
        } catch (Throwable $rollbackError) {
          return [
            'ok'=>false,
            'error'=>'تعذر إكمال العملية واسترجاع حالة حساب الشبكة تلقائياً: ' . $rollbackError->getMessage(),
          ];
        }
      }
      return ['ok'=>false, 'error'=>$e->getMessage()];
    }
  }
}

