<?php
// admin/automation_engine.php
// محرك أتمتة مركزي: (Warn / Limit / Cut / Restore) + Logs + Locks + منع التكرار

require_once __DIR__ . '/db_bootstrap.php';
require_once __DIR__ . '/helpers.php';
require_once __DIR__ . '/settings_store.php';
require_once __DIR__ . '/mikrotik_lib.php';
require_once __DIR__ . '/messages_lib.php';
require_once __DIR__ . '/whatsapp_cloud.php';
require_once __DIR__ . '/billing_cycle_lib.php';
require_once __DIR__ . '/finance_daily_cashbox_lib.php';

/**
 * جداول الأتمتة: logs + actions (منع التكرار) + locks
 */
function automation_ensure_tables(mysqli $conn): void {
  $conn->query("CREATE TABLE IF NOT EXISTS automation_logs (
    id INT AUTO_INCREMENT PRIMARY KEY,
    task_key VARCHAR(60) NOT NULL,
    status VARCHAR(20) NOT NULL,
    message TEXT NULL,
    meta_json LONGTEXT NULL,
    started_at DATETIME NULL DEFAULT CURRENT_TIMESTAMP,
    finished_at DATETIME NULL,
    KEY idx_task (task_key),
    KEY idx_started (started_at)
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");

  $conn->query("CREATE TABLE IF NOT EXISTS automation_actions (
    id INT AUTO_INCREMENT PRIMARY KEY,
    action_key VARCHAR(30) NOT NULL,
    subscriber_id INT NULL,
    invoice_id INT NULL,
    cycle_month VARCHAR(7) NULL,
    created_at DATETIME NULL DEFAULT CURRENT_TIMESTAMP,
    UNIQUE KEY uq_action (action_key, invoice_id, cycle_month),
    KEY idx_sub (subscriber_id)
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");

  $conn->query("CREATE TABLE IF NOT EXISTS automation_locks (
    lock_key VARCHAR(60) PRIMARY KEY,
    token VARCHAR(64) NOT NULL,
    locked_until DATETIME NOT NULL
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
}

function automation_log_start(mysqli $conn, string $taskKey, array $meta = []): int {
  automation_ensure_tables($conn);
  $tk = $conn->real_escape_string($taskKey);
  $mj = $conn->real_escape_string(json_encode($meta, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
  $conn->query("INSERT INTO automation_logs (task_key,status,message,meta_json) VALUES ('$tk','RUNNING',NULL,'$mj')");
  return (int)$conn->insert_id;
}

function automation_log_finish(mysqli $conn, int $logId, string $status, string $message = '', array $meta = []): void {
  $st = $conn->real_escape_string($status);
  $msg = $conn->real_escape_string($message);
  $mj = $conn->real_escape_string(json_encode($meta, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
  $conn->query("UPDATE automation_logs SET status='$st', message='$msg', meta_json='$mj', finished_at=NOW() WHERE id=".(int)$logId);
}

function automation_lock_acquire(mysqli $conn, string $lockKey, int $ttlSeconds = 240): ?string {
  automation_ensure_tables($conn);
  $token = bin2hex(random_bytes(16));
  $lk = $conn->real_escape_string($lockKey);
  $tk = $conn->real_escape_string($token);
  $ttlSeconds = max(30, min(3600, (int)$ttlSeconds));
  $conn->query("DELETE FROM automation_locks WHERE lock_key='$lk' AND locked_until < NOW()");
  $ok = $conn->query("INSERT INTO automation_locks (lock_key,token,locked_until)
                      VALUES ('$lk','$tk', DATE_ADD(NOW(), INTERVAL $ttlSeconds SECOND))");
  return $ok ? $token : null;
}

function automation_lock_release(mysqli $conn, string $lockKey, string $token): void {
  $lk = $conn->real_escape_string($lockKey);
  $tk = $conn->real_escape_string($token);
  $conn->query("DELETE FROM automation_locks WHERE lock_key='$lk' AND token='$tk'");
}

function automation_should_run(mysqli $conn, string $taskKey, int $intervalMinutes): bool {
  $intervalMinutes = max(1, (int)$intervalMinutes);
  $last = (int)get_app_setting($conn, 'auto_last_'.$taskKey, '0');
  $now = time();
  return ($now - $last) >= ($intervalMinutes * 60);
}

function automation_mark_ran(mysqli $conn, string $taskKey): void {
  set_app_setting($conn, 'auto_last_'.$taskKey, (string)time());
}

/**
 * إرسال رسالة من template_key باستخدام WhatsApp Cloud (إن كان مُعد)
 */
function automation_send_template(mysqli $conn, string $templateKey, string $toPhone, array $vars, ?int $subscriberId = null): array {
  require_once __DIR__ . '/messages_lib.php';
  ensure_message_queue_tables($conn);

  // enqueue by default (more stable than direct send inside cron/automation)
  $uid = null;
  $enq = messages_enqueue_template($conn, $templateKey, $toPhone, $vars, $subscriberId, null, $uid);
  if (empty($enq['ok'])) return $enq;

  // optional: immediate send for small systems
  $sendNow = (int)get_app_setting($conn, 'msg_send_immediately', '0') === 1;
  if ($sendNow) {
    $run = messages_process_queue($conn, 20);
    return ['ok'=>true, 'queued_id'=>$enq['id'] ?? null, 'run'=>$run];
  }
  return ['ok'=>true,'queued_id'=>$enq['id'] ?? null,'status'=>'QUEUED'];
}

/**
 * مهمة: إنفاذ القطع/التخفيض/التحذير حسب الفواتير غير المدفوعة
 */
function automation_task_unpaid_enforce(mysqli $conn): array {
  $queued = function_exists('billing_queue_requests')
    ? billing_queue_requests($conn)
    : ['reminder_pending'=>0,'limit_pending'=>0,'cut_pending'=>0,'skipped'=>0,'errors'=>1];
  return [
    'warned'=>(int)($queued['reminder_pending'] ?? 0),
    'limited'=>(int)($queued['limit_pending'] ?? 0),
    'cut'=>(int)($queued['cut_pending'] ?? 0),
    'restored'=>0,
    'skipped'=>(int)($queued['skipped'] ?? 0),
    'errors'=>(int)($queued['errors'] ?? 0),
    'mode'=>'approval_only',
  ];
}

/**
 * Run all enabled tasks once (respecting intervals unless force=true)
 */


/**
 * مهمة: توليد فواتير الشهر تلقائياً (BASE) لكل المشتركين
 * - تمنع التكرار لنفس الشهر
 * - تربط المبلغ بسعر السرعة من app_settings عبر: price_speed_{speed_name}
 */
function automation_task_generate_monthly_invoices(mysqli $conn): array {
  if (!function_exists('billing_generate_current_cycle_invoices')) {
    return ['created'=>0,'skipped'=>0,'errors'=>1,'error'=>'billing_cycle_service_unavailable'];
  }
  return billing_generate_current_cycle_invoices($conn, false);
}

function automation_task_daily_cashbox_close(mysqli $conn): array {
  return finance_daily_auto_close($conn);
}

/**
 * مهمة: تقرير أسبوعي (مُلخص) — يُسجَّل في automation_logs ويمكن لاحقاً إرساله كرسالة/واتساب
 */
function automation_task_weekly_report(mysqli $conn): array {
  $out = [
    'from'=>date('Y-m-d', strtotime('-7 days')),
    'to'=>date('Y-m-d'),
    'invoices_total'=>0,
    'paid_total'=>0,
    'unpaid_count'=>0,
    'subs_total'=>0
  ];

  $hasInvoices = ($conn->query("SHOW TABLES LIKE 'invoices'") && $conn->query("SHOW TABLES LIKE 'invoices'")->num_rows>0);
  if($hasInvoices){
    $dateCol = 'created_at';
    $rs = $conn->query("SHOW COLUMNS FROM invoices LIKE 'created_at'");
    if(!$rs || !$rs->num_rows){
      $rs2 = $conn->query("SHOW COLUMNS FROM invoices LIKE 'date'");
      if($rs2 && $rs2->num_rows) $dateCol = 'date';
    }

    $from = $out['from'];
    $to   = $out['to'];

    $st = @$conn->prepare("SELECT SUM(COALESCE(amount,0)) AS s FROM invoices WHERE DATE($dateCol) BETWEEN ? AND ?");
    if($st){
      $st->bind_param('ss',$from,$to);
      if($st->execute()){
        $r = $st->get_result(); $out['invoices_total'] = (float)($r->fetch_assoc()['s'] ?? 0);
      }
      $st->close();
    }

    $hasPaidAmount = ($conn->query("SHOW COLUMNS FROM invoices LIKE 'paid_amount'") && $conn->query("SHOW COLUMNS FROM invoices LIKE 'paid_amount'")->num_rows>0);
    if($hasPaidAmount){
      $st2 = @$conn->prepare("SELECT SUM(COALESCE(paid_amount,0)) AS s FROM invoices WHERE DATE($dateCol) BETWEEN ? AND ?");
      if($st2){
        $st2->bind_param('ss',$from,$to);
        if($st2->execute()){
          $r = $st2->get_result(); $out['paid_total'] = (float)($r->fetch_assoc()['s'] ?? 0);
        }
        $st2->close();
      }
    } else {
      $st3 = @$conn->prepare("SELECT SUM(COALESCE(amount,0)) AS s FROM invoices WHERE status='مدفوع' AND DATE($dateCol) BETWEEN ? AND ?");
      if($st3){
        $st3->bind_param('ss',$from,$to);
        if($st3->execute()){
          $r = $st3->get_result(); $out['paid_total'] = (float)($r->fetch_assoc()['s'] ?? 0);
        }
        $st3->close();
      }
    }

    $rUn = @$conn->query("SELECT COUNT(*) AS c FROM invoices WHERE status<>'مدفوع'");
    if($rUn) $out['unpaid_count'] = (int)($rUn->fetch_assoc()['c'] ?? 0);
  }

  $hasSubs = ($conn->query("SHOW TABLES LIKE 'subscribers'") && $conn->query("SHOW TABLES LIKE 'subscribers'")->num_rows>0);
  if($hasSubs){
    $rS = @$conn->query("SELECT COUNT(*) AS c FROM subscribers WHERE COALESCE(is_deleted,0)=0");
    if($rS) $out['subs_total'] = (int)($rS->fetch_assoc()['c'] ?? 0);
  }

  // إرسال واتساب للإدارة (اختياري)
  $sendWa = get_app_setting($conn, 'automation_reports_send_whatsapp', '0') === '1';
  $adminPhone = trim((string)get_app_setting($conn, 'automation_reports_admin_phone', ''));
  if($sendWa && $adminPhone !== ''){
    $res = automation_send_template($conn, 'ADMIN_WEEKLY_REPORT', $adminPhone, [
      'from' => $out['from'],
      'to' => $out['to'],
      'invoices_total' => $out['invoices_total'],
      'paid_total' => $out['paid_total'],
      'unpaid_count' => $out['unpaid_count'],
      'subs_total' => $out['subs_total'],
    ], null);
    $out['whatsapp'] = $res['ok'] ? 'sent' : ('failed: '.($res['error'] ?? ''));
  }

  return $out;
}

/**
 * مهمة: صيانة تلقائية (تنظيف سجلات قديمة + Optimize best-effort)
 */
function automation_task_maintenance(mysqli $conn): array {
  $out = ['pruned'=>[], 'optimized'=>[], 'errors'=>0];

  $pruneDaysAuto = (int)get_app_setting($conn,'automation_logs_prune_days','90');
  $pruneDaysMsg  = (int)get_app_setting($conn,'message_logs_prune_days','180');

  @$conn->query("DELETE FROM automation_logs WHERE started_at < DATE_SUB(NOW(), INTERVAL ".max(7,$pruneDaysAuto)." DAY)");
  $out['pruned']['automation_logs'] = (int)$conn->affected_rows;

  if($conn->query("SHOW TABLES LIKE 'message_logs'") && $conn->query("SHOW TABLES LIKE 'message_logs'")->num_rows>0){
    // message_logs قد لا يحتوي created_at في كل النسخ، نحاول best-effort
    $hasCreated = ($conn->query("SHOW COLUMNS FROM message_logs LIKE 'created_at'") && $conn->query("SHOW COLUMNS FROM message_logs LIKE 'created_at'")->num_rows>0);
    if($hasCreated){
      @$conn->query("DELETE FROM message_logs WHERE created_at < DATE_SUB(NOW(), INTERVAL ".max(7,$pruneDaysMsg)." DAY)");
      $out['pruned']['message_logs'] = (int)$conn->affected_rows;
    }
  }

  foreach(['invoices','subscribers','message_logs','automation_logs','automation_actions'] as $t){
    $te = $conn->real_escape_string($t);
    $exists = $conn->query("SHOW TABLES LIKE '$te'");
    if($exists && $exists->num_rows){
      if(@$conn->query("OPTIMIZE TABLE `$t`")){
        $out['optimized'][] = $t;
      }
    }
  }

  return $out;
}


/**
 * مهمة: تحديث مطابقة المستقبلات كل 6 ساعات عبر محرك الأتمتة الداخلي.
 * لا تعمل من الداشبورد ولا تضيف أوامر متكررة؛ الدالة نفسها تحتوي بوابة زمنية.
 */
function automation_task_receiver_match_refresh(mysqli $conn): array {
  require_once __DIR__ . '/receiver_match_refresh_lib.php';
  if(!function_exists('ua_receiver_match_run_due')){
    return ['ok'=>false,'error'=>'receiver match refresh library unavailable'];
  }
  return ua_receiver_match_run_due($conn, 21600, false);
}

function automation_run(mysqli $conn, bool $force = false): array {
  automation_ensure_tables($conn);
  $results = [];

  // tasks config (fallbacks)
  $tasks = [
    'daily_cashbox_close' => [
      'enabled_key' => 'automation_daily_cashbox_enabled',
      'interval_key' => 'automation_daily_cashbox_interval',
      'enabled_default' => '1',
      'interval_default' => '15',
      'fn' => 'automation_task_daily_cashbox_close',
    ],
    'cut_enforce' => [
      'enabled_key' => 'automation_cut_enabled',
      'interval_key' => 'automation_cut_interval',
      'enabled_default' => '1',
      'interval_default' => '15',
      'fn' => 'automation_task_unpaid_enforce',
    ],

'invoices_generate' => [
  'enabled_key' => 'automation_invoices_enabled',
  'interval_key' => 'automation_invoices_interval',
  'enabled_default' => '1',
  'interval_default' => '1440',
  'fn' => 'automation_task_generate_monthly_invoices',
],
'weekly_report' => [
  'enabled_key' => 'automation_reports_enabled',
  'interval_key' => 'automation_reports_interval',
  'enabled_default' => '1',
  'interval_default' => '10080',
  'fn' => 'automation_task_weekly_report',
],
'maintenance' => [
  'enabled_key' => 'automation_maintenance_enabled',
  'interval_key' => 'automation_maintenance_interval',
  'enabled_default' => '1',
  'interval_default' => '1440',
  'fn' => 'automation_task_maintenance',
],
'receiver_match_refresh' => [
  'enabled_key' => 'automation_receiver_match_enabled',
  'interval_key' => 'automation_receiver_match_interval',
  'enabled_default' => '1',
  'interval_default' => '360',
  'fn' => 'automation_task_receiver_match_refresh',
],
    'quota_runner' => [
      'enabled_key' => 'automation_sync_enabled',
      'interval_key' => 'automation_sync_interval',
      'enabled_default' => '1',
      'interval_default' => '15',
      'fn' => function(mysqli $conn){
        // quota_runner.php returns array
        return include __DIR__ . '/quota_runner.php';
      },
    ],
  ];

  foreach($tasks as $taskKey => $cfg){
    $enabled = get_app_setting($conn, (string)$cfg['enabled_key'], (string)$cfg['enabled_default']) === '1';
    $interval = (int)get_app_setting($conn, (string)$cfg['interval_key'], (string)$cfg['interval_default']);

    if(!$enabled){
      $results[$taskKey] = ['skipped'=>true,'reason'=>'disabled'];
      continue;
    }

    if(!$force && !automation_should_run($conn, $taskKey, $interval)){
      $results[$taskKey] = ['skipped'=>true,'reason'=>'interval'];
      continue;
    }

    $lockToken = automation_lock_acquire($conn, 'task_'.$taskKey, 300);
    if(!$lockToken){
      $results[$taskKey] = ['skipped'=>true,'reason'=>'locked'];
      continue;
    }

    $logId = automation_log_start($conn, $taskKey, ['interval_min'=>$interval,'force'=>$force]);
    try {
      $fn = $cfg['fn'];
      $out = is_string($fn) ? $fn($conn) : $fn($conn);
      automation_mark_ran($conn, $taskKey);
      automation_log_finish($conn, $logId, 'OK', 'done', is_array($out)?$out:['result'=>$out]);
      $results[$taskKey] = ['ok'=>true,'result'=>$out];
    } catch(Throwable $e){
      automation_log_finish($conn, $logId, 'ERROR', $e->getMessage(), ['trace'=>substr($e->getTraceAsString(),0,1500)]);
      $results[$taskKey] = ['ok'=>false,'error'=>$e->getMessage()];
    } finally {
      automation_lock_release($conn, 'task_'.$taskKey, $lockToken);
    }
  }

  return $results;
}
