<?php
// /edenrms/ajax/approval_decide.php
// JSON-only, schema-aware approval endpoint
declare(strict_types=1);
@date_default_timezone_set('Asia/Colombo');

/* ---------------- JSON wrappers & hardening ---------------- */
header('Content-Type: application/json; charset=utf-8');
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

ob_start();

$reply = function(array $p, int $code = 200) {
  while (ob_get_level()) { @ob_end_clean(); }
  http_response_code($code);
  header('Content-Type: application/json; charset=utf-8');
  echo json_encode($p, JSON_UNESCAPED_UNICODE);
  exit;
};

set_exception_handler(function(Throwable $e) use ($reply) {
  $reply([
    'ok'      => false,
    'error'   => 'EXCEPTION',
    'message' => $e->getMessage()
  ], 500);
});

register_shutdown_function(function() use ($reply) {
  $fatal = error_get_last();
  if ($fatal && in_array($fatal['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR], true)) {
    $reply([
      'ok'      => false,
      'error'   => 'FATAL',
      'message' => $fatal['message']
    ], 500);
  }

  $raw = ob_get_contents();
  if ($raw === false) return;

  foreach (headers_list() as $h) {
    if (stripos($h, 'Location:') === 0) {
      header_remove('Location');
      http_response_code(200);
      break;
    }
  }

  @ob_end_clean();
  header('Content-Type: application/json; charset=utf-8', true);

  $clean = ltrim($raw, "\xEF\xBB\xBF \r\n\t ");
  $s = strpos($clean, '{');
  $e = strrpos($clean, '}');

  if ($s !== false && $e !== false && $e > $s) {
    echo substr($clean, $s, $e - $s + 1);
  } else {
    echo json_encode([
      'ok'    => false,
      'error' => 'NON_JSON_OUTPUT',
      'raw'   => mb_substr($clean, 0, 200)
    ], JSON_UNESCAPED_UNICODE);
  }
});

/* ---------------- Session & auth ---------------- */
if (session_status() !== PHP_SESSION_ACTIVE) { @session_start(); }

$ROOT = dirname(__DIR__);
require_once $ROOT . '/includes/auth.php';

/* Treat this endpoint as AJAX: send JSON 401 on not-logged-in */
$loggedIn = function_exists('ec_is_logged_in')
  ? (bool)ec_is_logged_in()
  : (!empty($_SESSION['user']) || !empty($_SESSION['admin']) || !empty($_SESSION['super_admin']) || !empty($_SESSION['logged_in']));

if (!$loggedIn) {
  $reply(['ok' => false, 'error' => 'AUTH', 'message' => 'Please sign in'], 401);
}

/* RBAC: only allow approvers */
$role = strtolower((string)($_SESSION['user']['role'] ?? $_SESSION['role'] ?? ''));
$allow = in_array($role, ['super_admin','admin','branch_admin','manager','accountant','coordinator'], true)
      || (function_exists('ec_can') && (ec_can('students.manage') || ec_can('students.view')));

if (!$allow) {
  $reply(['ok' => false, 'error' => 'FORBIDDEN', 'message' => 'Insufficient permission'], 403);
}

$uid = (int)($_SESSION['user']['id'] ?? $_SESSION['admin']['id'] ?? $_SESSION['super_admin']['id'] ?? $_SESSION['id'] ?? 0);
if ($uid <= 0) {
  $reply(['ok' => false, 'error' => 'AUTH', 'message' => 'User id missing'], 401);
}

/* ---------------- DB connection ---------------- */
$mysqli = function_exists('ec_db') ? ec_db() : null;
if (!$mysqli instanceof mysqli) {
  $inc = $ROOT . '/includes/db.php';
  if (is_file($inc)) {
    require_once $inc;
    foreach (['conn','con','mysqli','db','link'] as $v) {
      if (isset($$v) && $$v instanceof mysqli) {
        $mysqli = $$v;
        break;
      }
    }
  }
}
if (!$mysqli instanceof mysqli) {
  $reply(['ok' => false, 'error' => 'DB', 'message' => 'DB connection failed'], 500);
}
$mysqli->set_charset('utf8mb4');

/* ---------------- Helpers ---------------- */
function now(): string {
  return date('Y-m-d H:i:s');
}

function tExists(mysqli $db, string $t): bool {
  $sql = "SELECT 1
          FROM INFORMATION_SCHEMA.TABLES
          WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?
          LIMIT 1";
  $st = $db->prepare($sql);
  $st->bind_param('s', $t);
  $st->execute();
  $st->store_result();
  $ok = $st->num_rows > 0;
  $st->free_result();
  $st->close();
  return $ok;
}

function cExists(mysqli $db, string $t, string $c): bool {
  if (!tExists($db, $t)) return false;
  $sql = "SELECT 1
          FROM INFORMATION_SCHEMA.COLUMNS
          WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ?
          LIMIT 1";
  $st = $db->prepare($sql);
  $st->bind_param('ss', $t, $c);
  $st->execute();
  $st->store_result();
  $ok = $st->num_rows > 0;
  $st->free_result();
  $st->close();
  return $ok;
}

function pickCol(mysqli $db, string $tbl, array $cands): ?string {
  foreach ($cands as $c) {
    if (cExists($db, $tbl, $c)) return $c;
  }
  return null;
}

function schedTable(mysqli $db): string {
  if (tExists($db, 'payment_schedule')) return 'payment_schedule';
  if (tExists($db, 'installments'))     return 'installments';
  return 'payment_schedule';
}

function ensurePaymentSchedule(mysqli $db): string {
  if (tExists($db, 'payment_schedule')) return 'payment_schedule';
  if (tExists($db, 'installments'))     return 'installments';

  $db->query("CREATE TABLE IF NOT EXISTS payment_schedule (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    student_id INT UNSIGNED NOT NULL,
    installment_no INT UNSIGNED NOT NULL,
    course_fee DECIMAL(12,2) NOT NULL DEFAULT 0,
    reg_fee DECIMAL(12,2) NOT NULL DEFAULT 0,
    cert_fee DECIMAL(12,2) NOT NULL DEFAULT 0,
    due_date DATE NULL,
    status ENUM('PENDING','PAID','OVERDUE') NOT NULL DEFAULT 'PENDING',
    created_at DATETIME NOT NULL,
    INDEX (student_id),
    INDEX (installment_no),
    INDEX (status),
    INDEX (due_date)
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");

  return 'payment_schedule';
}

/* ---------------- Input ---------------- */
$id   = (int)($_POST['id'] ?? $_GET['id'] ?? $_POST['request_id'] ?? 0);
$act  = strtolower((string)($_POST['decision'] ?? $_POST['action'] ?? $_GET['action'] ?? $_POST['do'] ?? ''));
$note = trim((string)($_POST['note'] ?? ''));

if ($id <= 0) {
  $reply(['ok' => false, 'error' => 'INPUT', 'message' => 'Missing id'], 422);
}
if (!in_array($act, ['approve', 'reject'], true)) {
  $reply(['ok' => false, 'error' => 'INPUT', 'message' => 'Invalid action'], 422);
}

/* ---------------- Load approval request ---------------- */
if (!tExists($mysqli, 'approval_requests')) {
  $reply(['ok' => false, 'error' => 'SCHEMA', 'message' => 'Table approval_requests not found'], 500);
}

$st = $mysqli->prepare("SELECT * FROM approval_requests WHERE id=? LIMIT 1");
$st->bind_param('i', $id);
$st->execute();
$req = $st->get_result()->fetch_assoc();
$st->close();

if (!$req) {
  $reply(['ok' => false, 'error' => 'NOT_FOUND', 'message' => 'Request not found'], 404);
}

/* tolerant aliases */
$g = fn(...$keys) => (function($a, $keys) {
  foreach ($keys as $k) {
    if (array_key_exists($k, $a) && $a[$k] !== null) return $a[$k];
  }
  return null;
})($req, $keys);

$type       = strtolower((string)($g('type','req_type','request_type') ?? ''));
$status     = strtolower((string)($g('status','state','approval_status') ?? ''));
$student_id = (int)($g('student_id','sid','student','studentid','std_id','entity_id') ?? 0);
$ref_id     = (int)($g('ref_id','request_id','rid') ?? 0);

$payloadRaw = (string)($g('payload','data','json') ?? '');
$payload    = $payloadRaw !== '' ? (json_decode($payloadRaw, true) ?: []) : [];

/* fallback student id from payload for delete requests */
if ($student_id <= 0 && isset($payload['student_id'])) {
  $student_id = (int)$payload['student_id'];
}

/* ---------------- Notification loader ---------------- */
$noti = $ROOT . '/includes/notifications.php';
if (is_file($noti)) {
  @require_once $noti;
}

/* ---------------- Decision writer ---------------- */
$writeDecision = function(string $finalStatus) use ($mysqli, $id, $uid, $note) {
  $tbl = 'approval_requests';
  $updates = [];

  if      (cExists($mysqli, $tbl, 'status')) $updates['status'] = $finalStatus;
  else if (cExists($mysqli, $tbl, 'state'))  $updates['state']  = $finalStatus;

  if (cExists($mysqli, $tbl, 'decided_at')) $updates['decided_at'] = now();
  if (cExists($mysqli, $tbl, 'updated_at')) $updates['updated_at'] = now();
  if (cExists($mysqli, $tbl, 'decided_by')) $updates['decided_by'] = (int)$uid;
  if ($note !== '' && cExists($mysqli, $tbl, 'note')) $updates['note'] = $note;

  if (!$updates) return;

  $sets = [];
  $types = '';
  $vals = [];

  foreach ($updates as $col => $val) {
    $sets[] = "`$col` = ?";
    if (is_int($val)) {
      $types .= 'i';
      $vals[] = $val;
    } else {
      $types .= 's';
      $vals[] = (string)$val;
    }
  }

  $types .= 'i';
  $vals[] = $id;

  $sql = "UPDATE `$tbl` SET " . implode(', ', $sets) . " WHERE id=?";
  $u = $mysqli->prepare($sql);
  $u->bind_param($types, ...$vals);
  $u->execute();
  $u->close();
};

/* ---------------- Reject path ---------------- */
if ($act === 'reject') {
  $writeDecision('rejected');

  if (function_exists('noti_from_approval_updated')) {
    try { noti_from_approval_updated($mysqli, $id, 'rejected', $uid); } catch (Throwable $e) {}
  }

  $reply([
    'ok'      => true,
    'status'  => 'rejected',
    'applied' => false,
    'message' => 'Request rejected'
  ]);
}

/* already approved */
if ($status === 'approved') {
  $reply([
    'ok'      => true,
    'status'  => 'approved',
    'applied' => false,
    'message' => 'Already approved'
  ]);
}

/* ---------------- APPROVE : delete_student ---------------- */
if ($type === 'delete_student') {
  if ($student_id <= 0) {
    $reply(['ok' => false, 'error' => 'INPUT', 'message' => 'Delete request has no student id'], 422);
  }

  $stuTbl = tExists($mysqli, 'studentss') ? 'studentss' : (tExists($mysqli, 'students') ? 'students' : null);
  if (!$stuTbl) {
    $reply(['ok' => false, 'error' => 'SCHEMA', 'message' => 'Students table not found'], 500);
  }

  $mysqli->begin_transaction();
  try {
    $photo = '';
    if (cExists($mysqli, $stuTbl, 'photograph')) {
      $q = $mysqli->prepare("SELECT photograph FROM `$stuTbl` WHERE id=? LIMIT 1");
      $q->bind_param('i', $student_id);
      $q->execute();
      $q->bind_result($photo);
      $q->fetch();
      $q->close();
      $photo = (string)$photo;
    }

    $centerRegNo = '';
    $refNo       = '';
    $refNo2      = '';

    $pickCols = [];
    if (cExists($mysqli, $stuTbl, 'center_reg_no')) $pickCols[] = 'center_reg_no';
    if (cExists($mysqli, $stuTbl, 'reference_no'))  $pickCols[] = 'reference_no';
    if (cExists($mysqli, $stuTbl, 'reference_no2')) $pickCols[] = 'reference_no2';

    if ($pickCols) {
      $sql = "SELECT " . implode(',', $pickCols) . " FROM `$stuTbl` WHERE id=? LIMIT 1";
      $q = $mysqli->prepare($sql);
      $q->bind_param('i', $student_id);
      $q->execute();
      $row = $q->get_result()->fetch_assoc() ?: [];
      $q->close();

      $centerRegNo = (string)($row['center_reg_no'] ?? '');
      $refNo       = (string)($row['reference_no'] ?? '');
      $refNo2      = (string)($row['reference_no2'] ?? '');
    }

    $deleteMap = [
      ['payments', 'student_id'],
      ['payment_schedule', 'student_id'],
      ['installments', 'student_id'],
      ['student_fee_plans', 'student_id'],
      ['student_batches', 'student_id'],
      ['student_courses', 'student_id'],
      ['student_certifications', 'student_id'],
      ['student_verifications', 'student_id'],
      ['student_exams', 'student_id'],
      ['student_extra_fees', 'student_id'],
      ['certifications', 'student_id'],
      ['enrollments', 'student_id'],
      ['lms_enrollments', 'student_id'],
      ['lms_assessments', 'student_id'],
      ['lms_results', 'student_id'],
    ];

    foreach ($deleteMap as [$t, $c]) {
      if (cExists($mysqli, $t, $c)) {
        $x = $mysqli->prepare("DELETE FROM `$t` WHERE `$c`=?");
        $x->bind_param('i', $student_id);
        $x->execute();
        $x->close();
      }
    }

    if (tExists($mysqli, 'student_numbers')) {
      if (cExists($mysqli, 'student_numbers', 'student_id')) {
        $x = $mysqli->prepare("DELETE FROM `student_numbers` WHERE `student_id`=?");
        $x->bind_param('i', $student_id);
        $x->execute();
        $x->close();
      }

      if ($centerRegNo !== '' && cExists($mysqli, 'student_numbers', 'center_reg_no')) {
        $x = $mysqli->prepare("DELETE FROM `student_numbers` WHERE `center_reg_no`=?");
        $x->bind_param('s', $centerRegNo);
        $x->execute();
        $x->close();
      }

      if ($refNo !== '' && cExists($mysqli, 'student_numbers', 'reference_no')) {
        $x = $mysqli->prepare("DELETE FROM `student_numbers` WHERE `reference_no`=?");
        $x->bind_param('s', $refNo);
        $x->execute();
        $x->close();
      }

      if ($refNo2 !== '' && cExists($mysqli, 'student_numbers', 'reference_no2')) {
        $x = $mysqli->prepare("DELETE FROM `student_numbers` WHERE `reference_no2`=?");
        $x->bind_param('s', $refNo2);
        $x->execute();
        $x->close();
      }
    }

    $d = $mysqli->prepare("DELETE FROM `$stuTbl` WHERE id=? LIMIT 1");
    $d->bind_param('i', $student_id);
    $d->execute();
    $deletedRows = $d->affected_rows;
    $d->close();

    $writeDecision('approved');
    $mysqli->commit();

    if ($deletedRows > 0 && $photo !== '') {
      $uploads = realpath($ROOT . '/uploads');
      if ($uploads) {
        $path = $uploads . DIRECTORY_SEPARATOR . basename($photo);
        if (is_file($path)) @unlink($path);
      }
    }

    if (function_exists('noti_from_approval_updated')) {
      try { noti_from_approval_updated($mysqli, $id, 'approved', $uid); } catch (Throwable $e) {}
    }

    $reply([
      'ok'      => true,
      'status'  => 'approved',
      'applied' => true,
      'deleted' => true,
      'message' => 'Student deleted successfully'
    ]);
  } catch (Throwable $e) {
    try { $mysqli->rollback(); } catch (Throwable $e2) {}
    $reply(['ok' => false, 'error' => 'DELETE_FAILED', 'message' => $e->getMessage()], 500);
  }
}

/* ---------------- APPROVE : plan requests ---------------- */
$applyPlan = in_array($type, ['custom_plan','custom_installment_plan','custom_installments','installment_plan'], true);

if ($applyPlan) {
  $items = [];

  if (tExists($mysqli, 'custom_plan_request_items') && $ref_id > 0) {
    $q = $mysqli->prepare(
      "SELECT installment_no, due_date, course_fee_lkr, registration_fee_lkr, certificate_fee_usd
         FROM custom_plan_request_items
        WHERE request_id=?
        ORDER BY installment_no, id"
    );
    $q->bind_param('i', $ref_id);
    $q->execute();
    $rs = $q->get_result();
    while ($row = $rs->fetch_assoc()) $items[] = $row;
    $q->close();
  }

  if (!$items && isset($payload['items']) && is_array($payload['items'])) {
    $items = $payload['items'];
  }

  if (!$items && isset($payload['rows']) && is_array($payload['rows'])) {
    foreach ($payload['rows'] as $r) {
      $items[] = [
        'installment_no'       => (int)($r['n'] ?? $r['no'] ?? 0),
        'due_date'             => $r['due_date'] ?? null,
        'course_fee_lkr'       => (float)($r['course_fee'] ?? $r['course'] ?? $r['course_lkr'] ?? 0),
        'registration_fee_lkr' => (float)($r['reg_fee'] ?? $r['registration_fee'] ?? $r['reg_lkr'] ?? 0),
        'certificate_fee_usd'  => (float)($r['cert_fee'] ?? $r['certificate_fee'] ?? $r['cert_usd'] ?? 0),
      ];
    }
  }

  if ($student_id <= 0) {
    $reply(['ok' => false, 'error' => 'INPUT', 'message' => 'Missing student id for plan request'], 422);
  }

  if (!$items) {
    $reply(['ok' => false, 'error' => 'INPUT', 'message' => 'No installment rows found'], 422);
  }

  $tbl = ensurePaymentSchedule($mysqli);
  $mysqli->begin_transaction();

  try {
    $del = $mysqli->prepare("DELETE FROM `$tbl` WHERE student_id=?");
    $del->bind_param('i', $student_id);
    $del->execute();
    $del->close();

    if ($tbl === 'payment_schedule') {
      $insertSql = "INSERT INTO payment_schedule
        (student_id, installment_no, course_fee, reg_fee, cert_fee, due_date, status, created_at)
        VALUES (?, ?, ?, ?, ?, ?, 'PENDING', ?)";
    } else {
      $insertSql = "INSERT INTO installments
        (student_id, installment_no, course_fee, reg_fee, cert_fee, due_date, status, created_at)
        VALUES (?, ?, ?, ?, ?, ?, 'PENDING', ?)";
    }

    $ins = $mysqli->prepare($insertSql);

    foreach ($items as $r) {
      $n   = (int)($r['installment_no'] ?? $r['n'] ?? $r['no'] ?? 0);
      $cf  = (float)($r['course_fee_lkr'] ?? $r['course_fee'] ?? $r['course'] ?? 0);
      $rf  = (float)($r['registration_fee_lkr'] ?? $r['reg_fee'] ?? $r['registration_fee'] ?? 0);
      $usd = (float)($r['certificate_fee_usd'] ?? $r['cert_fee'] ?? $r['certificate_fee'] ?? 0);
      $due = trim((string)($r['due_date'] ?? ''));
      $due = $due !== '' ? $due : null;
      $created = now();

      $ins->bind_param('iidddss', $student_id, $n, $cf, $rf, $usd, $due, $created);
      $ins->execute();
    }

    $ins->close();
    $writeDecision('approved');
    $mysqli->commit();

    if (function_exists('noti_from_approval_updated')) {
      try { noti_from_approval_updated($mysqli, $id, 'approved', $uid); } catch (Throwable $e) {}
    }

    $reply([
      'ok'      => true,
      'status'  => 'approved',
      'applied' => true,
      'message' => 'Plan approved successfully'
    ]);
  } catch (Throwable $e) {
    try { $mysqli->rollback(); } catch (Throwable $e2) {}
    $reply(['ok' => false, 'error' => 'PLAN_APPLY_FAILED', 'message' => $e->getMessage()], 500);
  }
}

/* ---------------- APPROVE : default ---------------- */
try {
  $writeDecision('approved');

  if (function_exists('noti_from_approval_updated')) {
    try { noti_from_approval_updated($mysqli, $id, 'approved', $uid); } catch (Throwable $e) {}
  }

  $reply([
    'ok'      => true,
    'status'  => 'approved',
    'applied' => true,
    'message' => 'Request approved successfully'
  ]);
} catch (Throwable $e) {
  $reply(['ok' => false, 'error' => 'APPROVE_FAILED', 'message' => $e->getMessage()], 500);
}