<?php
/**
 * change-password.php — Supports:
 * 1) Forced change flow (must_change_password=1 / $_SESSION['force_change'])
 * 2) Profile flow (POST dari profile.php) → redirect dengan pass_success/pass_error
 */
session_start();
require_once __DIR__ . '/db.php';

if (!isset($_SESSION['user'])) {
    $LOGIN_PAGE = file_exists(__DIR__.'/login.php') ? 'login.php' : 'index.php';
    header("Location: $LOGIN_PAGE"); exit;
}

$user     = $_SESSION['user'];
$userId   = (int)($user['id'] ?? 0);
$staffNo  = (string)($user['staff_number'] ?? '');
if ($userId <= 0) { header("Location: dashboard.php"); exit; }

/* ===== CSRF token (share dengan profile.php) ===== */
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(16));
}
$CSRF = $_SESSION['csrf_token'];

/* ===== Helper mini ===== */
function column_exists(mysqli $conn, string $table, string $column): bool {
    $sql = "SELECT COUNT(*) AS c
            FROM INFORMATION_SCHEMA.COLUMNS
            WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ?";
    $stmt = $conn->prepare($sql);
    $stmt->bind_param('ss', $table, $column);
    $stmt->execute();
    $res = $stmt->get_result()->fetch_assoc();
    $stmt->close();
    return (int)$res['c'] > 0;
}
function table_exists(mysqli $conn, string $table): bool {
    $sql = "SELECT COUNT(*) AS c
            FROM INFORMATION_SCHEMA.TABLES
            WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?";
    $stmt = $conn->prepare($sql);
    $stmt->bind_param('s', $table);
    $stmt->execute();
    $res = $stmt->get_result()->fetch_assoc();
    $stmt->close();
    return (int)$res['c'] > 0;
}

/* ===== Dapatkan status paksa tukar + hash semasa ===== */
$flag = 0; $currentHash = ''; $fullName = ''; $email = ''; $ic = '';
$stmt = $conn->prepare("SELECT password_hash, must_change_password, full_name, email, ic_number FROM members WHERE id=? LIMIT 1");
$stmt->bind_param("i", $userId);
$stmt->execute();
$res = $stmt->get_result();
if ($row = $res->fetch_assoc()) {
    $flag        = (int)$row['must_change_password'];
    $currentHash = (string)$row['password_hash'];
    $fullName    = (string)($row['full_name'] ?? '');
    $email       = (string)($row['email'] ?? '');
    $ic          = (string)($row['ic_number'] ?? '');
}
$stmt->close();

/* ===== Kenal pasti "Profile mode" vs "Forced mode" ===== */
$isPost         = ($_SERVER['REQUEST_METHOD'] === 'POST');
$fromProfile    = $isPost && (isset($_POST['old_password']) || (!isset($_POST['current_password']) && isset($_POST['csrf_token'])));
$forcedFlow     = ($flag === 1 || !empty($_SESSION['force_change']));

/* ===== Jika bukan forced dan bukan request POST dari profile → keluar ===== */
if (!$forcedFlow && !$fromProfile) {
    header("Location: dashboard.php"); exit;
}

/* ===== Validasi kekuatan (2 polisi) ===== */
function strong_enough_forced(string $pwd): bool {
    return strlen($pwd) >= 8
        && preg_match('/[A-Z]/', $pwd)
        && preg_match('/[a-z]/', $pwd)
        && preg_match('/[0-9]/', $pwd);
}
function strong_enough_profile(string $pwd): ?string {
    if (strlen($pwd) < 8)                           return 'Password must be at least 8 characters.';
    if (!preg_match('/[A-Z]/', $pwd))               return 'Use at least one uppercase letter.';
    if (!preg_match('/[a-z]/', $pwd))               return 'Use at least one lowercase letter.';
    if (!preg_match('/\d/', $pwd))                  return 'Include at least one number.';
    if (!preg_match('/[\W_]/', $pwd))               return 'Include at least one symbol.';
    if (preg_match('/(.)\1{2,}/', $pwd))            return 'Avoid repeating the same character 3+ times.';
    $s = strtolower($pwd);
    $bad = ['0123','1234','2345','3456','4567','5678','6789','abcd','bcde','cdef','defg','qwer','asdf','zxcv'];
    foreach ($bad as $b) { if (strpos($s,$b)!==false) return 'Avoid easy sequences like 1234/abcd/qwer.'; }
    return null;
}

/* ===== Util redirect balik ke profile ===== */
function redirect_profile_ok(string $msg){ header("Location: profile.php?pass_success=".rawurlencode($msg)); exit; }
function redirect_profile_err(string $msg){ header("Location: profile.php?pass_error=".rawurlencode($msg)); exit; }

/* ===== Proses POST (kedua-dua mode) ===== */
if ($isPost) {
    $csrf = (string)($_POST['csrf_token'] ?? '');
    if (!hash_equals($_SESSION['csrf_token'] ?? '', $csrf)) {
        if ($fromProfile) redirect_profile_err('Invalid CSRF token.');
        $err = 'Sesi tamat atau token tidak sah. Sila cuba lagi.';
    } else {
        $old = (string)($_POST['old_password'] ?? $_POST['current_password'] ?? '');
        $new = (string)($_POST['new_password'] ?? '');
        $cfm = (string)($_POST['confirm_password'] ?? '');

        if ($old === '' || $new === '' || $cfm === '') {
            if ($fromProfile) redirect_profile_err('Please fill in all required fields.');
            $err = 'Sila isi semua medan.';
        } elseif (!password_verify($old, $currentHash)) {
            if ($fromProfile) redirect_profile_err('Old password is incorrect.');
            $err = 'Kata laluan semasa tidak tepat.';
        } elseif ($new !== $cfm) {
            if ($fromProfile) redirect_profile_err('New password and confirmation do not match.');
            $err = 'Pengesahan kata laluan baharu tidak sepadan.';
        } else {
            if ($fromProfile) {
                $why = strong_enough_profile($new);
                if ($why !== null) { redirect_profile_err($why); }
                if (password_verify($new, $currentHash)) {
                    redirect_profile_err('New password must be different from the old one.');
                }
            } else {
                if (!strong_enough_forced($new)) {
                    $err = 'Kata laluan baharu tidak cukup kuat.';
                }
            }

            if (empty($err)) {
                $hash = password_hash($new, PASSWORD_DEFAULT);
                $sql = column_exists($conn, 'members', 'must_change_password')
                     ? "UPDATE members SET password_hash=?, must_change_password=0 WHERE id=? LIMIT 1"
                     : "UPDATE members SET password_hash=? WHERE id=? LIMIT 1";
                $upd  = $conn->prepare($sql);
                $upd->bind_param("si", $hash, $userId);

                if ($upd->execute()) {
                    $upd->close();

                    if (function_exists('write_audit')) { @write_audit($conn, $userId, $userId, $fromProfile ? "USER_CHANGED_PASSWORD_FROM_PROFILE" : "USER_CHANGED_PASSWORD_AFTER_FORCED_RESET"); }
                    elseif (table_exists($conn,'audit_logs')) {
                        $audit = $conn->prepare("INSERT INTO audit_logs (admin_id, member_id, action, timestamp) VALUES (?, ?, ?, NOW())");
                        if ($audit) {
                            $action = $fromProfile ? 'Changed password (self via profile)' : 'Changed password (forced reset)';
                            $adminId = $userId; $memberId = $userId;
                            $audit->bind_param('iis', $adminId, $memberId, $action);
                            $audit->execute(); $audit->close();
                        }
                    }

                    unset($_SESSION['force_change']);
                    session_regenerate_id(true);

                    if ($fromProfile) {
                        redirect_profile_ok('Password changed successfully.');
                    } else {
                        header("Location: dashboard.php"); exit;
                    }
                } else {
                    $dbErr = $conn->error;
                    if ($fromProfile) redirect_profile_err('Failed to update password. '.$dbErr);
                    $err = 'Ralat semasa menyimpan kata laluan baharu. Sila cuba lagi.';
                }
            }
        }
    }
}

if ($fromProfile) { exit; } // tidak render UI untuk profile mode
?>
<!DOCTYPE html>
<html lang="ms">
<head>
<meta charset="utf-8">
<title>KPPRSAJ DIS — Tukar Kata Laluan</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
  * { box-sizing: border-box; }
  body {
    margin: 0; padding: 0; font-family: Arial, sans-serif;
    background: linear-gradient(rgba(0,0,0,0.55), rgba(0,0,0,0.55)),
                url('assets/img/background.jpg') no-repeat center center fixed;
    background-size: cover;
    display: flex; justify-content: center; align-items: center;
    min-height: 100vh;
  }
  .box { background:#fff; padding:24px; border-radius:16px; width:840px; max-width:92vw;
         box-shadow:0 8px 24px rgba(0,0,0,0.28); border:1px solid #e9ecef; }
  .hdr { display:flex; align-items:center; gap:12px; margin-bottom:14px; padding-bottom:12px; border-bottom:1px solid #f1f3f5; }
  .logo { width:54px; }
  .system-name { font-size:12px; color:#6c757d }
  h2 { margin:4px 0 0; font-size:20px }
  .lede { color:#495057; margin: 6px 0 16px; }
  .grid { display:grid; grid-template-columns: 1fr 1fr; gap: 18px; }
  @media (max-width: 820px){ .grid { grid-template-columns: 1fr; } }
  label { display:block; margin:0 0 6px; font-weight:700; color:#333; }
  .field { position:relative; }
  .input { width:100%; padding:12px 12px; border-radius:10px; border:1px solid #ced4da; background:#fff9c4; }
  .tiny-btn{ background:#f8f9fa; border:1px solid #ced4da; color:#0d6efd; height:30px; padding:0 10px; border-radius:6px; cursor:pointer; font-size:12px; }
  .actions-inline { position:absolute; right:8px; top:50%; transform:translateY(-50%); display:flex; gap:6px; align-items:center; }
  .alert { padding:10px 12px; border-radius:8px; margin-bottom:12px; font-size:.95em; }
  .alert-error { background:#fdecea; color:#c62828; border:1px solid #f5c6cb }
  .alert-ok    { background:#ecfdf5; color:#065f46; border:1px solid #a7f3d0 }
  .row { display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-top:6px }
  .meter { display:flex; gap:6px } .seg{ width:54px; height:10px; border-radius:999px; background:#e9ecef; }
  .seg.on.weak{ background:#f87171 } .seg.on.fair{ background:#fbbf24 } .seg.on.good{ background:#34d399 } .seg.on.strong{ background:#22c55e }
  .pill{ font-size:12px; padding:4px 8px; border-radius:999px; border:1px solid #dee2e6; }
  .pill.weak{ color:#dc2626; border-color:#fecaca; background:#fff5f5 }
  .pill.fair{ color:#b45309; border-color:#fde68a; background:#fffbeb }
  .pill.good{ color:#047857; border-color:#bbf7d0; background:#ecfdf5 }
  .pill.strong{ color:#065f46; border-color:#a7f3d0; background:#ecfdf5 }
  .rules { font-size:13px; color:#555; margin:10px 0 2px; padding-left:18px } .rules li{ margin:4px 0 }
  .caps { display:none; font-size:12px; color:#b45309; background:#fffbeb; border:1px dashed #fcd34d; padding:6px 8px; border-radius:6px }
  .match { font-size:12px; margin-top:6px }
  .actions { margin-top: 18px; display:flex; gap:10px; justify-content:flex-end; }
  .btn { padding:10px 14px; border-radius:8px; border:1px solid #ced4da; background:#fff; color:#212529; cursor:pointer; font-weight:600; }
  .btn-primary { background:#007bff; color:#fff; border-color:#007bff; }
</style>
</head>
<body>
  <div class="box">
    <div class="hdr">
      <img src="assets/img/kpprsaj-logo.png" class="logo" alt="KPPRSAJ">
      <div>
        <div class="system-name">Database Information System</div>
        <h2>TUKAR KATA LALUAN</h2>
      </div>
    </div>

    <p class="lede">Hi, <strong><?= htmlspecialchars($fullName ?? '') ?></strong>. Akaun anda memerlukan penetapan kata laluan baharu sebelum meneruskan.</p>

    <?php if (!empty($err)): ?><div class="alert alert-error"><?= htmlspecialchars($err) ?></div><?php endif; ?>
    <?php if (!empty($ok)):  ?><div class="alert alert-ok"><?= htmlspecialchars($ok) ?></div><?php endif; ?>

    <form method="post" autocomplete="off" id="pwdForm" novalidate>
      <input type="hidden" name="csrf_token" value="<?= htmlspecialchars($CSRF) ?>">

      <div class="grid">
        <!-- Current -->
        <div>
          <label for="cur">Kata Laluan Semasa</label>
          <div class="field">
            <input class="input" id="cur" name="current_password" type="password" required>
            <!-- KEMAS: butang toggle dalam actions-inline -->
            <div class="actions-inline" id="actions-cur">
              <button type="button" class="tiny-btn toggle" data-target="cur">Lihat</button>
            </div>
          </div>
          <div style="font-size:12px; color:#6c757d; margin-top:6px">Jika anda tidak pasti, hubungi admin sistem.</div>
        </div>

        <!-- New -->
        <div>
          <label for="new1">Kata Laluan Baharu</label>
          <div class="field">
            <input class="input" id="new1" name="new_password" type="password" required aria-describedby="rulesList tips pill">
            <div class="actions-inline" id="actions-new1">
              <button type="button" class="tiny-btn" id="genBtn" title="Jana kata laluan kuat">Jana</button>
              <button type="button" class="tiny-btn toggle" data-target="new1">Lihat</button>
            </div>
          </div>

          <!-- Strength -->
          <div class="row">
            <div class="meter" aria-hidden="true">
              <div class="seg" id="seg1"></div>
              <div class="seg" id="seg2"></div>
              <div class="seg" id="seg3"></div>
              <div class="seg" id="seg4"></div>
            </div>
            <span id="pill" class="pill">Kekuatan: —</span>
            <span id="capsWarn" class="caps">Caps Lock aktif</span>
          </div>

          <!-- Rules -->
          <ul class="rules" id="rulesList">
            <li data-rule="len">Sekurang-kurangnya <strong>8 aksara</strong></li>
            <li data-rule="upper">Sekurang-kurangnya <strong>1 huruf besar (A–Z)</strong></li>
            <li data-rule="lower">Sekurang-kurangnya <strong>1 huruf kecil (a–z)</strong></li>
            <li data-rule="num">Sekurang-kurangnya <strong>1 nombor (0–9)</strong></li>
            <li data-rule="sym" class="optional">(Pilihan) simbol <code>!@#$%^&*</code> untuk tambah kekuatan</li>
            <li data-rule="repeat" class="optional">Elak corak berulang/berturutan</li>
          </ul>
          <div id="tips" style="font-size:12px;color:#6c757d">Tip: guna frasa unik + nombor + simbol. Elak maklumat peribadi.</div>
        </div>
      </div>

      <!-- Confirm -->
      <div style="margin-top:14px">
        <label for="new2">Sahkan Kata Laluan Baharu</label>
        <div class="field">
          <input class="input" id="new2" name="confirm_password" type="password" required>
          <!-- KEMAS: butang toggle dalam actions-inline -->
          <div class="actions-inline" id="actions-new2">
            <button type="button" class="tiny-btn toggle" data-target="new2">Lihat</button>
          </div>
        </div>
        <div id="matchMsg" class="match"></div>
      </div>

      <div class="actions">
        <a class="btn" href="logout.php">Log Keluar</a>
        <button id="submitBtn" class="btn btn-primary" type="submit" disabled>Simpan</button>
      </div>
    </form>
  </div>

<script>
(function(){
  const cur  = document.getElementById('cur');
  const new1 = document.getElementById('new1');
  const new2 = document.getElementById('new2');

  const actionsCur  = document.getElementById('actions-cur');
  const actionsNew1 = document.getElementById('actions-new1');
  const actionsNew2 = document.getElementById('actions-new2');

  const segs = [document.getElementById('seg1'),document.getElementById('seg2'),document.getElementById('seg3'),document.getElementById('seg4')];
  const pill = document.getElementById('pill');
  const rules = document.querySelectorAll('#rulesList li[data-rule]');
  const capsWarn = document.getElementById('capsWarn');
  const matchMsg = document.getElementById('matchMsg');
  const submitBtn = document.getElementById('submitBtn');
  const form = document.getElementById('pwdForm');
  const genBtn = document.getElementById('genBtn');

  function syncPadding(inputEl, actionsEl){
    if (!inputEl || !actionsEl) return;
    const w = actionsEl.offsetWidth + 16;
    inputEl.style.paddingRight = w + 'px';
  }
  function syncPaddingAll(){
    syncPadding(cur,  actionsCur);
    syncPadding(new1, actionsNew1);
    syncPadding(new2, actionsNew2);
  }
  function syncPaddingByBtn(btn){
    const target = document.getElementById(btn.dataset.target);
    const actions = btn.closest('.actions-inline');
    syncPadding(target, actions);
  }

  // Toggle "Lihat/Sembunyi"
  document.querySelectorAll('.toggle').forEach(btn=>{
    btn.addEventListener('click', ()=>{
      const target = document.getElementById(btn.dataset.target);
      if (!target) return;
      target.type = (target.type === 'password') ? 'text' : 'password';
      btn.textContent = (target.type === 'password') ? 'Lihat' : 'Sembunyi';
      target.focus();
      syncPaddingByBtn(btn); // teks butang berubah → lebar berubah → re-sync
    });
  });

  // Generator kata laluan
  function generatePassword(){
    const upp="ABCDEFGHJKLMNPQRSTUVWXYZ", low="abcdefghijkmnopqrstuvwxyz", num="23456789", sym="!@#$%^&*";
    const pools=[upp,low,num,sym], pick=s=>s[Math.floor(Math.random()*s.length)], len=Math.floor(Math.random()*5)+14;
    let out = pick(upp)+pick(low)+pick(num)+pick(sym);
    while(out.length<len){ const p=pools[Math.floor(Math.random()*pools.length)]; out+=pick(p); }
    out = out.split(''); for(let i=out.length-1;i>0;i--){ const j=Math.floor(Math.random()*(i+1)); [out[i],out[j]]=[out[j],out[i]]; }
    return out.join('');
  }
  genBtn.addEventListener('click', ()=>{ const pwd = generatePassword(); new1.value = pwd; new2.value = pwd; refresh(); });

  function scorePassword(p){
    let score=0; const len=p.length, hasUpper=/[A-Z]/.test(p), hasLower=/[a-z]/.test(p), hasNum=/[0-9]/.test(p), hasSym=/[^A-Za-z0-9]/.test(p);
    const repeated=/(.)\1{2,}/.test(p), sequential=/(0123|1234|2345|3456|4567|5678|6789|abcd|bcde|cdef|defg|qwer|asdf|zxcv)/i.test(p);
    const variety=[hasUpper,hasLower,hasNum,hasSym].filter(Boolean).length;
    if (len>=8) score++; if (len>=12) score++; if (variety>=3) score++; if (variety===4) score++; if (repeated||sequential) score=Math.max(0,score-1);
    return {score,len,hasUpper,hasLower,hasNum,hasSym,repeated,sequential};
  }
  function updateRules(m){ rules.forEach(li=>{ const r=li.getAttribute('data-rule'); let ok=false;
    if (r==='len') ok=m.len>=8; if (r==='upper') ok=m.hasUpper; if (r==='lower') ok=m.hasLower; if (r==='num') ok=m.hasNum;
    if (r==='sym') ok=m.hasSym; if (r==='repeat') ok=!(m.repeated||m.sequential); li.classList.toggle('ok', ok);
  });}
  function setSegments(score){
    segs.forEach((s,i)=>{ s.className='seg'; if(i<score){ s.classList.add('on', score<=1?'weak':score===2?'fair':score===3?'good':'strong'); }});
    pill.className='pill ' + (score<=1?'weak':score===2?'fair':score===3?'good':'strong');
    pill.textContent='Kekuatan: ' + (score<=1?'Lemah':score===2?'Sederhana':score===3?'Baik':'Kuat');
  }
  function updateMatch(){
    if (!new2.value){ matchMsg.textContent=''; matchMsg.className='match'; return false; }
    const ok = new1.value === new2.value; matchMsg.textContent = ok?'Pengesahan sepadan.':'Pengesahan tidak sepadan.'; matchMsg.className='match '+(ok?'ok':'err'); return ok;
  }
  function meetsMandatory(m){ return m.len>=8 && m.hasUpper && m.hasLower && m.hasNum; }
  function refresh(){ const m=scorePassword(new1.value); updateRules(m); setSegments(m.score); const ok=updateMatch(); submitBtn.disabled = !(meetsMandatory(m) && ok); syncPaddingAll(); }

  ['input','keyup'].forEach(evt=>{ new1.addEventListener(evt, refresh); new2.addEventListener(evt, refresh); });
  window.addEventListener('resize', syncPaddingAll);

  // Initial
  refresh();
  syncPaddingAll();
})();
</script>
</body>
</html>
