<?php
/**
 * توابع کمکی مشترک برنامه
 */

if (session_status() === PHP_SESSION_NONE) {
    session_start();
}

require_once __DIR__ . '/../config.php';

// -------------------------------------------------------------------
// احراز هویت
// -------------------------------------------------------------------

function current_user() {
    return $_SESSION['user'] ?? null;
}

function require_login() {
    global $pdo;
    if (!current_user()) {
        header('Location: ' . base_url('login.php'));
        exit;
    }
    // برای کارمندان، مطالعه و تایید قرارداد/تعهدنامه و تکمیل پروفایل الزامی است
    $user = current_user();
    if ($user['role'] === 'employee') {
        $script = basename($_SERVER['SCRIPT_NAME']);
        $exempt = ['agreement.php', 'logout.php', 'complete_profile.php'];
        if (!in_array($script, $exempt)) {
            $currentVersion = (int)get_setting($pdo, 'agreement_version', 1);
            $stmt = $pdo->prepare("SELECT agreement_accepted_version FROM users WHERE id = ?");
            $stmt->execute([$user['id']]);
            $accepted = (int)$stmt->fetchColumn();
            if ($accepted < $currentVersion) {
                header('Location: ' . base_url('employee/agreement.php'));
                exit;
            }
            if (!profile_is_complete($pdo, $user['id'])) {
                header('Location: ' . base_url('employee/complete_profile.php'));
                exit;
            }
        }
    }
}

// -------------------------------------------------------------------
// قالب‌های ظاهری قابل انتخاب کاربر (پروفایل من)
// -------------------------------------------------------------------
function ui_templates() {
    return [
        'modern'  => ['label' => 'مدرن نرم', 'desc' => 'نوار کناری روشن، کارت‌های گرد با سایه‌ی نرم و تصویرسازی رنگی — طراحی پیش‌فرض سامانه.'],
        'classic' => ['label' => 'کلاسیک اداری', 'desc' => 'نوار ابزار بالای صفحه به‌جای نوار کناری، گوشه‌های تیز و چیدمانی فشرده و رسمی.'],
        'minimal' => ['label' => 'مینیمال', 'desc' => 'بدون سایه و گرادیان، خطوط باریک به‌جای کادر، فضای خالی بیشتر و رنگ کم‌رنگ‌تر.'],
    ];
}

function ui_colors() {
    return [
        'teal'   => ['label' => 'فیروزه‌ای', 'swatch' => '#0891B2'],
        'green'  => ['label' => 'سبز', 'swatch' => '#16A34A'],
        'blue'   => ['label' => 'آبی', 'swatch' => '#2563EB'],
        'red'    => ['label' => 'قرمز', 'swatch' => '#E11D48'],
        'orange' => ['label' => 'نارنجی', 'swatch' => '#EA580C'],
        'purple' => ['label' => 'بنفش', 'swatch' => '#7C3AED'],
    ];
}

function require_role($role) {
    require_login();
    if (current_user()['role'] !== $role) {
        header('Location: ' . base_url('index.php'));
        exit;
    }
}

// -------------------------------------------------------------------
// تنظیمات سامانه (جدول settings، کلید-مقدار)
// -------------------------------------------------------------------
function get_setting($pdo, $key, $default = null) {
    static $cache = [];
    if (array_key_exists($key, $cache)) {
        return $cache[$key];
    }
    $stmt = $pdo->prepare("SELECT setting_value FROM settings WHERE setting_key = ? LIMIT 1");
    $stmt->execute([$key]);
    $val = $stmt->fetchColumn();
    $cache[$key] = ($val === false) ? $default : $val;
    return $cache[$key];
}

function set_setting($pdo, $key, $value) {
    $stmt = $pdo->prepare("INSERT INTO settings (setting_key, setting_value) VALUES (?, ?) ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value)");
    $stmt->execute([$key, $value]);
}

function base_url($path = '') {
    // مسیر ریشه پروژه را با توجه به محل نصب محاسبه می‌کند (چه در ریشه دامنه، چه در زیرپوشه)
    $scriptDir = dirname($_SERVER['SCRIPT_NAME']);
    // اگر داخل پوشه admin یا employee هستیم یک سطح بالاتر برویم
    if (basename($scriptDir) === 'admin' || basename($scriptDir) === 'employee') {
        $scriptDir = dirname($scriptDir);
    }
    $root = rtrim($scriptDir, '/'); // ممکن است خالی شود اگر نصب در ریشه دامنه باشد
    return $root . '/' . ltrim($path, '/');
}

// -------------------------------------------------------------------
// CSRF
// -------------------------------------------------------------------

function csrf_token() {
    if (empty($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
    }
    return $_SESSION['csrf_token'];
}

function csrf_field() {
    return '<input type="hidden" name="csrf_token" value="' . htmlspecialchars(csrf_token()) . '">';
}

function verify_csrf() {
    $token = $_POST['csrf_token'] ?? '';
    if (!$token || !hash_equals($_SESSION['csrf_token'] ?? '', $token)) {
        die('درخواست نامعتبر است. لطفاً صفحه را مجدد بارگذاری کرده و دوباره تلاش کنید.');
    }
}

// -------------------------------------------------------------------
// تبدیل تاریخ میلادی به شمسی (جلالی) - بدون وابستگی خارجی
// الگوریتم استاندارد تبدیل گرگوری به جلالی
// -------------------------------------------------------------------

function div7($a, $b) {
    return (int)floor($a / $b);
}

/**
 * تبدیل میلادی به شمسی (الگوریتم استاندارد و تست‌شده)
 */
function gtoj($gy, $gm, $gd) {
    $g_d_m = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
    $jy = ($gy <= 1600) ? 0 : 979;
    $gy -= ($gy <= 1600) ? 621 : 1600;
    $gy2 = ($gm > 2) ? ($gy + 1) : $gy;
    $days = (365 * $gy) + div7(($gy2 + 3), 4) - div7(($gy2 + 99), 100) + div7(($gy2 + 399), 400) - 80 + $gd + $g_d_m[$gm - 1];
    $jy += 33 * div7($days, 12053);
    $days %= 12053;
    $jy += 4 * div7($days, 1461);
    $days %= 1461;
    if ($days > 365) {
        $jy += div7(($days - 1), 365);
        $days = ($days - 1) % 365;
    }
    if ($days < 186) {
        $jm = 1 + div7($days, 31);
        $jd = 1 + ($days % 31);
    } else {
        $jm = 7 + div7(($days - 186), 30);
        $jd = 1 + (($days - 186) % 30);
    }
    return [$jy, $jm, $jd];
}

/** سال شمسی جاری را برمی‌گرداند */
function current_jalali_year() {
    [$jy, , ] = gtoj((int)date('Y'), (int)date('n'), (int)date('j'));
    return $jy;
}

/** تبدیل ارقام لاتین به ارقام فارسی در هر رشته */
function fa_num($str) {
    $latin = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
    $persian = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'];
    return str_replace($latin, $persian, (string)$str);
}

/** تاریخ میلادی را به رشته شمسی با ارقام لاتین تبدیل می‌کند (فقط برای مقداردهی اولیه ورودی‌های قابل ویرایش) */
function to_jalali_string_latin($datetime) {
    if (!$datetime) return '';
    $ts = is_numeric($datetime) ? $datetime : strtotime($datetime);
    [$jy, $jm, $jd] = gtoj((int)date('Y', $ts), (int)date('n', $ts), (int)date('j', $ts));
    return sprintf('%04d/%02d/%02d', $jy, $jm, $jd);
}

/** تاریخ میلادی را به رشته شمسی (Y/m/d) با ارقام فارسی تبدیل می‌کند */
function to_jalali_string($datetime) {
    if (!$datetime) return '-';
    $ts = is_numeric($datetime) ? $datetime : strtotime($datetime);
    [$jy, $jm, $jd] = gtoj((int)date('Y', $ts), (int)date('n', $ts), (int)date('j', $ts));
    return fa_num(sprintf('%04d/%02d/%02d', $jy, $jm, $jd));
}

/**
 * تبدیل شمسی به میلادی (معکوس gtoj) - برای محاسبه بازه سال شمسی به میلادی لازم است
 * برمی‌گرداند: [gy, gm, gd]
 */
function jtog($jy, $jm, $jd) {
    $jy += 1595;
    $days = -355668 + (365 * $jy) + (div7($jy, 33) * 8) + div7((($jy % 33) + 3), 4) + $jd
        + (($jm < 7) ? ($jm - 1) * 31 : (($jm - 7) * 30) + 186);
    $gy = 400 * div7($days, 146097);
    $days %= 146097;
    if ($days > 36524) {
        $days--;
        $gy += 100 * div7($days, 36524);
        $days %= 36524;
        if ($days >= 365) $days++;
    }
    $gy += 4 * div7($days, 1461);
    $days %= 1461;
    if ($days > 365) {
        $gy += div7(($days - 1), 365);
        $days = ($days - 1) % 365;
    }
    $gd = $days + 1;
    $leap = (($gy % 4 === 0 && $gy % 100 !== 0) || $gy % 400 === 0);
    $g_days_in_month = [31, $leap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
    $gm = 0;
    for ($i = 0; $i < 12; $i++) {
        if ($gd <= $g_days_in_month[$i]) { $gm = $i + 1; break; }
        $gd -= $g_days_in_month[$i];
    }
    return [$gy, $gm, $gd];
}

/** ابتدای سال شمسی مشخص را به‌صورت تایم‌استمپ یونیکس (میلادی) برمی‌گرداند */
function jalali_year_start_timestamp($jy) {
    [$gy, $gm, $gd] = jtog($jy, 1, 1);
    return mktime(0, 0, 0, $gm, $gd, $gy);
}

/** پایان سال شمسی مشخص (آخرین لحظه اسفند) را به‌صورت تایم‌استمپ یونیکس برمی‌گرداند */
function jalali_year_end_timestamp($jy) {
    return jalali_year_start_timestamp($jy + 1) - 1;
}

/**
 * نسبت تناسب سقف بر اساس تاریخ استخدام را محاسبه می‌کند.
 * طبق تنظیمات سامانه، این تناسب فقط برای سال اول استخدام اعمال می‌شود؛
 * از سال دوم به بعد همیشه ۱ (سقف کامل) برمی‌گردد.
 */
function get_hire_year_proration_ratio($pdo, $hire_date, $jalali_year) {
    if (!$hire_date) return 1.0;
    $hireTs = strtotime($hire_date);
    if (!$hireTs) return 1.0;

    [$hireJy, ] = gtoj((int)date('Y', $hireTs), (int)date('n', $hireTs), (int)date('j', $hireTs));

    // اگر استخدام قبل از این سال شمسی بوده، سقف کامل است (طبق تنظیم: فقط سال اول تناسب دارد)
    if ($hireJy < $jalali_year) return 1.0;
    // اگر استخدام بعد از این سال شمسی بوده (نباید رخ دهد)، تناسب صفر
    if ($hireJy > $jalali_year) return 0.0;

    $yearStart = jalali_year_start_timestamp($jalali_year);
    $yearEnd = jalali_year_end_timestamp($jalali_year);
    $effectiveStart = max($hireTs, $yearStart);
    $daysEmployed = (int)floor(($yearEnd - $effectiveStart) / 86400) + 1;
    $daysEmployed = max(0, min(365, $daysEmployed));

    return round($daysEmployed / 365, 4);
}

/**
 * رشته تاریخ شمسی به فرمت YYYY/MM/DD یا YYYY-MM-DD را اعتبارسنجی می‌کند.
 * در صورت معتبر نبودن null و در غیر این صورت [gy, gm, gd] معادل میلادی را برمی‌گرداند.
 */
function parse_jalali_date($str) {
    $str = trim((string)$str);
    if (!preg_match('/^(\d{4})[\/\-](\d{1,2})[\/\-](\d{1,2})$/', $str, $m)) {
        return null;
    }
    $jy = (int)$m[1]; $jm = (int)$m[2]; $jd = (int)$m[3];
    if ($jm < 1 || $jm > 12 || $jd < 1 || $jd > 31) {
        return null;
    }
    return jtog($jy, $jm, $jd);
}

/** بررسی می‌کند تاریخ شمسی داده‌شده در آینده نیست (امروز مجاز است) */
function is_jalali_date_not_future($str) {
    $g = parse_jalali_date($str);
    if (!$g) return false;
    [$gy, $gm, $gd] = $g;
    $ts = mktime(0, 0, 0, $gm, $gd, $gy);
    return $ts <= mktime(23, 59, 59, (int)date('n'), (int)date('j'), (int)date('Y'));
}

/** لیست فیلدهای الزامی پروفایل که کارمند باید تکمیل کند */
function required_profile_fields() {
    return ['gender', 'national_code', 'id_number', 'mobile', 'email', 'address', 'birth_date'];
}

/** انواع مدارک الزامی (۱ تا ۶) و اختیاری (سایر مدارک) - مشترک بین کارمند اصلی و افراد تبعی */
function required_document_types() {
    return [
        'national_card_front' => 'تصویر روی کارت ملی',
        'national_card_back' => 'تصویر پشت کارت ملی',
        'birth_cert_p1' => 'تصویر صفحه اول شناسنامه',
        'birth_cert_p2' => 'تصویر صفحه دوم شناسنامه',
        'birth_cert_p3' => 'تصویر صفحه سوم شناسنامه',
        'birth_cert_p4' => 'تصویر صفحه چهارم شناسنامه',
    ];
}
function optional_document_types() {
    return ['other' => 'سایر مدارک'];
}

function profile_is_complete($pdo, $user_id) {
    $stmt = $pdo->prepare("SELECT gender, national_code, id_number, mobile, email, address, birth_date, has_medical_condition, medical_history FROM users WHERE id = ?");
    $stmt->execute([$user_id]);
    $u = $stmt->fetch();
    if (!$u) return false;
    foreach (required_profile_fields() as $f) {
        if ($u[$f] === null || $u[$f] === '') return false;
    }
    if ($u['has_medical_condition'] === null) return false;
    if ((int)$u['has_medical_condition'] === 1 && trim((string)$u['medical_history']) === '') return false;

    $stmt2 = $pdo->prepare("SELECT DISTINCT doc_type FROM user_documents WHERE user_id = ?");
    $stmt2->execute([$user_id]);
    $uploaded = array_column($stmt2->fetchAll(), 'doc_type');
    foreach (array_keys(required_document_types()) as $type) {
        if (!in_array($type, $uploaded)) return false;
    }
    return true;
}

/** فیلدهای همیشه قابل ویرایش برای کارمند اصلی، حتی بعد از تکمیل اولیه پروفایل (مشابه افراد تبعی) */
function profile_editable_after_completion_fields() {
    return ['mobile', 'phone', 'email', 'address', 'has_medical_condition', 'medical_history'];
}

// -------------------------------------------------------------------
// فرمت مبلغ به ریال با جداکننده هزارگان
// -------------------------------------------------------------------
function format_amount($amount) {
    return fa_num(number_format((float)$amount)) . ' ریال';
}

/**
 * تاریخ ملاک محاسبه سقف را برمی‌گرداند: در صورت مشمول بودن کارمند به آزمایش بدو استخدام
 * و ثبت تاریخ آن، همان تاریخ ملاک است؛ در غیر این صورت تاریخ استخدام ملاک خواهد بود.
 */
function get_eligibility_anchor_date($hire_date, $pre_employment_test_required, $pre_employment_test_date) {
    if ($pre_employment_test_required && $pre_employment_test_date) {
        return $pre_employment_test_date;
    }
    return $hire_date;
}

/**
 * محاسبه مانده قابل دریافت (سقف فردی هر ردیف و سقف‌های ترکیبی)
 * برای یک کاربر (یا یکی از افراد تبعی او) در یک سال شمسی مشخص.
 *
 * نکات مهم:
 * - سقف بر اساس مبلغ نهایی قابل پرداخت (بعد از کسر فرانشیز و سهم افراد تبعی) مصرف می‌شود؛
 *   یعنی فرانشیز از سقف کم نمی‌شود، فقط از مبلغ پرداختی به فرد کم می‌شود.
 * - سقف‌ها بر اساس تاریخ استخدام کارمند (فقط سال اول) متناسب‌سازی می‌شوند؛
 *   افراد تبعی سقف مستقل و برابر با کارمند دارند (نه سهمی از سقف او).
 * - اگر کارمند مشمول آزمایش بدو استخدام باشد، تاریخ آن آزمایش به‌جای تاریخ استخدام ملاک محاسبه است.
 * - ردیف‌هایی که «حداقل ماه پس از استخدام» دارند (مثل زایمان)، از فرمول تناسب روزانه
 *   مستثنی هستند: تا رسیدن به آن مدت سقف صفر است، بعد از آن سقف کامل اعمال می‌شود.
 * - ردیف‌هایی که محدودیت جنسیتی دارند (مثل زایمان/ناباروری) فقط برای همان جنسیت سقف دارند.
 */
function get_user_balance($pdo, $user_id, $jalali_year = null, $dependent_id = null) {
    if ($jalali_year === null) {
        $jalali_year = current_jalali_year();
    }

    // تاریخ استخدام و جنسیت شخص (کارمند یا فرد تبعی)
    if ($dependent_id) {
        $stmt = $pdo->prepare("
            SELECT u.hire_date, u.pre_employment_test_required, u.pre_employment_test_date, d.gender
            FROM dependents d JOIN users u ON u.id = d.user_id
            WHERE d.id = ?
        ");
        $stmt->execute([$dependent_id]);
    } else {
        $stmt = $pdo->prepare("SELECT hire_date, pre_employment_test_required, pre_employment_test_date, gender FROM users WHERE id = ?");
        $stmt->execute([$user_id]);
    }
    $person = $stmt->fetch();
    $hire_date = $person['hire_date'] ?? null;
    $gender = $person['gender'] ?? null;
    $anchor_date = get_eligibility_anchor_date($hire_date, $person['pre_employment_test_required'] ?? 0, $person['pre_employment_test_date'] ?? null);
    $ratio = get_hire_year_proration_ratio($pdo, $anchor_date, $jalali_year);

    // مجموع مبالغ نهایی قابل پرداخت (بعد از فرانشیز) به تفکیک هر ردیف درمانی
    if ($dependent_id) {
        $stmt = $pdo->prepare("
            SELECT row_id, COALESCE(SUM(payable_amount), 0) AS used
            FROM claims
            WHERE dependent_id = ? AND claim_year = ? AND status IN ('approved','paid')
            GROUP BY row_id
        ");
        $stmt->execute([$dependent_id, $jalali_year]);
    } else {
        $stmt = $pdo->prepare("
            SELECT row_id, COALESCE(SUM(payable_amount), 0) AS used
            FROM claims
            WHERE user_id = ? AND claim_year = ? AND dependent_id IS NULL AND status IN ('approved','paid')
            GROUP BY row_id
        ");
        $stmt->execute([$user_id, $jalali_year]);
    }
    $used_by_row = [];
    foreach ($stmt->fetchAll() as $r) {
        $used_by_row[$r['row_id']] = (float)$r['used'];
    }

    // همه ردیف‌های فعال
    $rows = $pdo->query("SELECT * FROM insurance_rows WHERE is_active = 1 ORDER BY sort_order")->fetchAll();

    $result_rows = [];
    foreach ($rows as $row) {
        $used = $used_by_row[$row['id']] ?? 0;

        $blocked_reason = null;

        // محدودیت جنسیتی
        if ($row['gender_restriction'] === 'female_only' && $gender !== 'female') {
            $blocked_reason = 'مخصوص جنسیت زن است';
        } elseif ($row['gender_restriction'] === 'male_only' && $gender !== 'male') {
            $blocked_reason = 'مخصوص جنسیت مرد است';
        }

        if ($blocked_reason) {
            $effective_max = 0;
        } elseif ($row['min_months_after_hire'] !== null && $row['min_months_after_hire'] !== '') {
            // این ردیف از فرمول تناسب روزانه مستثنی است: تا رسیدن به حداقل مدت، سقف صفر؛ بعد از آن سقف کامل
            if (!$anchor_date) {
                $effective_max = 0;
                $blocked_reason = 'تاریخ استخدام/آزمایش بدو استخدام ثبت نشده است';
            } else {
                $eligibleTs = strtotime('+' . (int)$row['min_months_after_hire'] . ' months', strtotime($anchor_date));
                if ($eligibleTs > time()) {
                    $effective_max = 0;
                    $blocked_reason = 'حداقل ' . (int)$row['min_months_after_hire'] . ' ماه پس از تاریخ استخدام/آزمایش بدو استخدام فعال می‌شود';
                } else {
                    $effective_max = round((float)$row['max_amount']);
                }
            }
        } else {
            $effective_max = round((float)$row['max_amount'] * $ratio);
        }

        $result_rows[] = [
            'id' => $row['id'],
            'name' => $row['name'],
            'max_amount' => $effective_max,
            'full_max_amount' => (float)$row['max_amount'],
            'used' => $used,
            'remaining' => max(0, $effective_max - $used),
            'blocked_reason' => $blocked_reason,
        ];
    }

    // سقف‌های ترکیبی
    $caps = $pdo->query("SELECT * FROM combined_caps WHERE is_active = 1")->fetchAll();
    $result_caps = [];
    foreach ($caps as $cap) {
        $stmt2 = $pdo->prepare("SELECT row_id FROM combined_cap_rows WHERE cap_id = ?");
        $stmt2->execute([$cap['id']]);
        $row_ids = array_column($stmt2->fetchAll(), 'row_id');
        $used = 0;
        foreach ($row_ids as $rid) {
            $used += $used_by_row[$rid] ?? 0;
        }
        $effective_max = round((float)$cap['max_amount'] * $ratio);
        $result_caps[] = [
            'id' => $cap['id'],
            'name' => $cap['name'],
            'row_ids' => $row_ids,
            'max_amount' => $effective_max,
            'used' => $used,
            'remaining' => max(0, $effective_max - $used),
        ];
    }

    return ['rows' => $result_rows, 'caps' => $result_caps, 'year' => $jalali_year, 'proration_ratio' => $ratio];
}

/**
 * محاسبه حداکثر مبلغ قابل تایید برای یک درخواست جدید با در نظر گرفتن
 * سقف فردی ردیف و همه سقف‌های ترکیبی مرتبط با آن ردیف، فرانشیز، و در صورت
 * تعلق به فرد تبعی، سهم پرداختی شرکت.
 *
 * توجه: چون سقف بر اساس مبلغ «نهایی قابل پرداخت» (بعد از فرانشیز) مصرف می‌شود،
 * سقف مانده ابتدا به مبلغ ناخالص معادل (پیش از فرانشیز) تبدیل می‌شود تا مدیر بداند
 * حداکثر چه مبلغی را می‌تواند به‌عنوان «مبلغ تایید‌شده» ثبت کند.
 */
function calculate_approvable_amount($pdo, $user_id, $row_id, $requested_amount, $jalali_year, $dependent_id = null) {
    $balance = get_user_balance($pdo, $user_id, $jalali_year, $dependent_id);

    $row_remaining = null;
    $blocked_reason = null;
    foreach ($balance['rows'] as $r) {
        if ($r['id'] == $row_id) {
            $row_remaining = $r['remaining'];
            $blocked_reason = $r['blocked_reason'];
            break;
        }
    }
    if ($row_remaining === null) {
        $row_remaining = 0;
    }

    $franchise_percent = (float)get_setting($pdo, 'franchise_percent', 0);
    $dependent_share_percent = 100.0;
    if ($dependent_id) {
        $dependent_share_percent = (float)get_setting($pdo, 'dependent_share_percent', 100);
    }
    // ضریب تبدیل مبلغ ناخالص تایید‌شده به مبلغ نهایی قابل پرداخت (که سقف بر اساس آن مصرف می‌شود)
    $multiplier = (1 - $franchise_percent / 100) * ($dependent_share_percent / 100);

    // سقف‌های مرتبط را از فضای «قابل پرداخت» به فضای «ناخالص تایید‌شده» تبدیل می‌کنیم
    $limits_gross = [$multiplier > 0 ? ($row_remaining / $multiplier) : $row_remaining];
    $limiting_caps = [];
    foreach ($balance['caps'] as $cap) {
        if (in_array($row_id, $cap['row_ids'])) {
            $limits_gross[] = $multiplier > 0 ? ($cap['remaining'] / $multiplier) : $cap['remaining'];
            $limiting_caps[] = $cap['name'];
        }
    }

    $max_approvable_gross = min($limits_gross);
    $capped_amount = max(0, min((float)$requested_amount, $max_approvable_gross));
    $was_limited = $capped_amount < (float)$requested_amount;

    $franchise_amount = round($capped_amount * $franchise_percent / 100);
    $after_franchise = $capped_amount - $franchise_amount;
    $payable_amount = round($after_franchise * $dependent_share_percent / 100);

    return [
        'approved_amount' => round($capped_amount), // حداکثر مبلغ ناخالصی که مدیر می‌تواند به‌عنوان تایید‌شده وارد کند
        'was_limited' => $was_limited,
        'row_remaining' => $row_remaining,
        'blocked_reason' => $blocked_reason,
        'limiting_caps' => $limiting_caps,
        'franchise_percent' => $franchise_percent,
        'franchise_amount' => $franchise_amount,
        'dependent_share_percent' => $dependent_share_percent,
        'payable_amount' => max(0, $payable_amount), // مبلغ نهایی قابل پرداخت به کارمند
    ];
}

// -------------------------------------------------------------------
// آپلود فایل مدرک
// -------------------------------------------------------------------
function handle_document_upload($file, $claim_id) {
    if (!isset($file['error']) || $file['error'] === UPLOAD_ERR_NO_FILE) {
        return null;
    }
    if ($file['error'] !== UPLOAD_ERR_OK) {
        throw new Exception('خطا در آپلود فایل.');
    }
    if ($file['size'] > MAX_UPLOAD_SIZE) {
        throw new Exception('حجم فایل بیش از حد مجاز است (حداکثر ۵ مگابایت).');
    }
    $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
    if (!in_array($ext, ALLOWED_EXTENSIONS)) {
        throw new Exception('فرمت فایل مجاز نیست. فرمت‌های مجاز: ' . implode(', ', ALLOWED_EXTENSIONS));
    }

    $claim_dir = UPLOAD_DIR . '/' . $claim_id;
    if (!is_dir($claim_dir)) {
        mkdir($claim_dir, 0755, true);
    }

    $safe_name = bin2hex(random_bytes(8)) . '.' . $ext;
    $dest = $claim_dir . '/' . $safe_name;
    if (!move_uploaded_file($file['tmp_name'], $dest)) {
        throw new Exception('ذخیره فایل با خطا مواجه شد.');
    }

    return [
        'file_path' => $claim_id . '/' . $safe_name,
        'original_name' => $file['name'],
    ];
}

// -------------------------------------------------------------------
// متن پیش‌فرض تعهدنامه (شخصی‌سازی‌شده بر اساس اطلاعات شرکت)
// -------------------------------------------------------------------
function default_agreement_text($company_name, $insurer_name, $policy_number, $franchise_percent, $dependent_share_percent) {
    return "قرارداد و تعهدنامه استفاده از بیمه تکمیلی درمان پرسنل\n\n"
        . "شرکت: {$company_name} (در این سند «شرکت» نامیده می‌شود)\n"
        . "کارمند: مطابق مشخصات ثبت‌شده در سامانه (در این سند «کارمند» نامیده می‌شود)\n\n"
        . "مقدمه:\n"
        . "شرکت به‌منظور تأمین بخشی از هزینه‌های درمانی پرسنل خود، از طریق بیمه‌نامه تکمیلی درمان گروهی منعقد‌شده با {$insurer_name}"
        . ($policy_number ? " (شماره بیمه‌نامه {$policy_number})" : "")
        . "، این سامانه را برای مدیریت و پرداخت هزینه‌های درمانی پرسنل راه‌اندازی کرده است. "
        . "کارمند با ورود به سامانه و تایید این تعهدنامه، شرایط زیر را می‌پذیرد:\n\n"
        . "ماده ۱- سقف‌های تعهد: حداکثر مبلغ قابل بازپرداخت برای هر ردیف درمانی (و سقف‌های ترکیبی مربوطه) در سامانه مشخص شده و ممکن است توسط شرکت به‌روزرسانی شود. "
        . "کارمند موظف است پیش از ثبت درخواست، سقف باقیمانده خود را در سامانه بررسی کند.\n\n"
        . "ماده ۲- فرانشیز: طبق شرایط بیمه‌نامه، {$franchise_percent} درصد از هر هزینه تایید‌شده به عهده کارمند است و مابقی توسط شرکت پرداخت می‌شود.\n\n"
        . "ماده ۳- تناسب سقف با سابقه کار: چنانچه کارمند در میانه یک سال شمسی استخدام شده باشد، سقف او در همان سال اول به نسبت تعداد روزهای اشتغال از تاریخ استخدام تا پایان همان سال (نسبت به ۳۶۵ روز) محاسبه می‌شود. از سال دوم فعالیت به بعد، سقف کامل اعمال خواهد شد.\n\n"
        . "ماده ۴- افراد تحت تکفل (تبعی): در صورت فعال بودن این امکان توسط شرکت، کارمند می‌تواند همسر دائم، فرزندان و پدر/مادر خود را به‌عنوان افراد تبعی در سامانه ثبت کند. "
        . "هزینه‌های درمانی افراد تبعی مشمول سقف‌های مستقل و مشابه سقف خود کارمند است؛ با این حال شرکت تنها {$dependent_share_percent} درصد از مبلغ تایید‌شده (پس از کسر فرانشیز) را برای افراد تبعی پرداخت می‌کند و مابقی به عهده کارمند خواهد بود.\n\n"
        . "ماده ۵- مدارک لازم: کارمند موظف است به ازای هر قلم هزینه، تمامی مدارک لازم اعلام‌شده برای همان ردیف درمانی را به‌طور کامل در سامانه بارگذاری کند. عدم ارائه مدارک کامل می‌تواند منجر به رد یا تاخیر در بررسی درخواست شود.\n\n"
        . "ماده ۶- بررسی و تایید: بررسی مدارک و تعیین مبلغ نهایی قابل پرداخت بر عهده واحد مالی/مدیر شرکت است. مبلغ تایید‌شده ممکن است به دلیل رسیدن به سقف‌های تعیین‌شده، کمتر از مبلغ درخواستی کارمند باشد.\n\n"
        . "ماده ۷- صحت اطلاعات: کارمند متعهد است اطلاعات و مدارک ارائه‌شده واقعی و بدون تحریف باشد. ارائه مدارک مخدوش یا نادرست، علاوه بر رد درخواست، می‌تواند تبعات انضباطی و قانونی نیز داشته باشد.\n\n"
        . "ماده ۸- مدت اعتبار: این تعهدنامه تا زمان تغییر توسط شرکت یا پایان همکاری کارمند با شرکت معتبر است. در صورت تغییر متن این تعهدنامه توسط شرکت، کارمند موظف است نسخه جدید را مجدداً مطالعه و تایید کند.\n\n"
        . "کارمند با تایید این صفحه، اعلام می‌کند متن فوق را به‌طور کامل مطالعه کرده و با تمامی موارد آن موافق است.";
}

/** متن رضایت‌نامه ثبت فرد تبعی */
function dependent_consent_text($dependent_share_percent) {
    return "با ثبت فرد تبعی در سامانه، کارمند تایید می‌کند که:\n"
        . "۱. فرد معرفی‌شده یکی از موارد همسر دائم، فرزند، پدر یا مادر کارمند است.\n"
        . "۲. هزینه‌های درمانی این فرد مشمول سقف‌های مستقل (مشابه سقف خود کارمند) خواهد بود.\n"
        . "۳. شرکت تنها {$dependent_share_percent} درصد از مبلغ تایید‌شده (پس از کسر فرانشیز) را برای این فرد پرداخت می‌کند و مابقی هزینه به عهده کارمند است.\n"
        . "۴. اطلاعات ارائه‌شده صحیح است و در صورت نادرست بودن، مسئولیت آن بر عهده کارمند خواهد بود.";
}

/**
 * ثبت تنظیمات پیش‌فرض سامانه (فقط در صورت نبودِ قبلی) — برای نصب اولیه و ارتقا هر دو امن است
 */
function seed_default_settings($pdo, $company_name = 'شرکت', $insurer_name = 'شرکت بیمه طرف قرارداد', $policy_number = '') {
    $defaults = [
        'dependents_enabled' => '1',
        'franchise_percent' => '10',
        'dependent_share_percent' => '50',
        'agreement_version' => '1',
        'proration_first_year_only' => '1',
        'company_name' => $company_name,
        'insurer_name' => $insurer_name,
        'policy_number' => $policy_number,
        'admin_notification_emails' => '',
    ];
    foreach ($defaults as $k => $v) {
        $stmt = $pdo->prepare("INSERT IGNORE INTO settings (setting_key, setting_value) VALUES (?, ?)");
        $stmt->execute([$k, $v]);
    }

    // متن تعهدنامه را فقط اگر قبلاً ثبت نشده، با مقادیر شخصی‌سازی‌شده بساز
    $existing = get_setting($pdo, 'agreement_content', null);
    if ($existing === null) {
        $text = default_agreement_text($company_name, $insurer_name, $policy_number, 10, 50);
        set_setting($pdo, 'agreement_content', $text);
    }
}

/**
 * حذف کامل یک درخواست به همراه مدارک آپلود‌شده آن (برای پاک‌سازی داده‌های تستی)
 */
function delete_claim($pdo, $claim_id) {
    $stmt = $pdo->prepare("SELECT file_path FROM claim_documents WHERE claim_id = ?");
    $stmt->execute([$claim_id]);
    $docs = $stmt->fetchAll();

    foreach ($docs as $d) {
        $full = UPLOAD_DIR . '/' . $d['file_path'];
        if (file_exists($full)) {
            @unlink($full);
        }
    }
    $claimDir = UPLOAD_DIR . '/' . $claim_id;
    if (is_dir($claimDir)) {
        @rmdir($claimDir);
    }

    $pdo->prepare("DELETE FROM claim_documents WHERE claim_id = ?")->execute([$claim_id]);
    $pdo->prepare("DELETE FROM claims WHERE id = ?")->execute([$claim_id]);
}

/**
 * بررسی می‌کند آیا فرد تبعی حداقل یک درخواست ثبت‌شده دارد (برای جلوگیری از حذف ناایمن)
 */
function dependent_has_claims($pdo, $dependent_id) {
    $stmt = $pdo->prepare("SELECT COUNT(*) FROM claims WHERE dependent_id = ?");
    $stmt->execute([$dependent_id]);
    return (int)$stmt->fetchColumn() > 0;
}

/**
 * حذف کامل یک فرد تبعی به همراه مدارک آپلود‌شده او.
 * اگر درخواستی برای این فرد ثبت شده باشد، حذف انجام نمی‌شود (باید غیرفعال/نگه‌داشته شود).
 * @return bool موفقیت حذف
 */
function delete_dependent($pdo, $dependent_id) {
    if (dependent_has_claims($pdo, $dependent_id)) {
        return false;
    }

    $stmt = $pdo->prepare("SELECT file_path FROM dependent_documents WHERE dependent_id = ?");
    $stmt->execute([$dependent_id]);
    $docs = $stmt->fetchAll();

    foreach ($docs as $d) {
        $full = UPLOAD_DIR . '/' . $d['file_path'];
        if (file_exists($full)) {
            @unlink($full);
        }
    }
    $depDir = UPLOAD_DIR . '/dependent_' . $dependent_id;
    if (is_dir($depDir)) {
        @rmdir($depDir);
    }

    $pdo->prepare("DELETE FROM dependent_documents WHERE dependent_id = ?")->execute([$dependent_id]);
    $pdo->prepare("DELETE FROM dependents WHERE id = ?")->execute([$dependent_id]);
    return true;
}

/** آیا فایل بر اساس پسوند، تصویر است (برای نمایش پیش‌نمایش کوچک) */
function is_image_ext($filename) {
    return in_array(strtolower(pathinfo($filename, PATHINFO_EXTENSION)), ['jpg', 'jpeg', 'png']);
}

// -------------------------------------------------------------------
// ثبت لاگ فعالیت
// -------------------------------------------------------------------
function log_activity($pdo, $action, $details = null, $claim_id = null) {
    $user = current_user();
    $stmt = $pdo->prepare("INSERT INTO activity_log (user_id, claim_id, action, details) VALUES (?, ?, ?, ?)");
    $stmt->execute([$user['id'] ?? null, $claim_id, $action, $details]);
}

function status_label($status) {
    $map = [
        'pending' => 'در انتظار بررسی',
        'approved' => 'تایید شده',
        'rejected' => 'رد شده',
        'paid' => 'پرداخت شده',
        'needs_correction' => 'نیاز به اصلاح',
    ];
    return $map[$status] ?? $status;
}

function status_badge_class($status) {
    $map = [
        'pending' => 'bg-warning text-dark',
        'approved' => 'bg-success',
        'rejected' => 'bg-danger',
        'paid' => 'bg-primary',
        'needs_correction' => 'bg-danger',
    ];
    return $map[$status] ?? 'bg-secondary';
}

function dependent_status_label($status) {
    $map = [
        'pending' => 'در انتظار تایید مدیر',
        'approved' => 'تایید شده',
        'needs_correction' => 'نیاز به اصلاح',
    ];
    return $map[$status] ?? $status;
}

function dependent_status_badge_class($status) {
    $map = [
        'pending' => 'bg-warning text-dark',
        'approved' => 'bg-success',
        'needs_correction' => 'bg-danger',
    ];
    return $map[$status] ?? 'bg-secondary';
}

/** فیلدهای «ثابت» فرد تبعی که بعد از تایید مدیر دیگر قابل ویرایش نیستند */
function dependent_locked_fields() {
    return ['full_name', 'relation', 'national_code', 'id_number', 'gender', 'birth_date'];
}
