<?php
declare(strict_types=1);

/**
 * /register.php
 * Реєстрація користувача:
 * Обов'язкові: email, password, username, ПІБ, стать, дата народження
 * Необов'язкові: avatar (image), about, country (ISO select), city, education, workplace
 * Відправка листа активації.
 *
 * ОНОВЛЕНО:
 * - не прив’язано жорстко до users.is_active / users.activation_token_hash:
 *   визначає active/token/sent_at/activated_at колонки автоматично (як у /activate.php та адмінці)
 * - birthdate підтримує birthdate або birth_date
 * - safe INSERT: додає тільки ті колонки, які реально існують
 * - без “зливу” технічних помилок у flash
 * - дрібні підчистки/узгодження з нашими патернами (noindex, PRG, rate-limit, blocked IP reason)
 */

require_once __DIR__ . '/includes/start.php';
require_once __DIR__ . '/includes/mailer.php';
require_once __DIR__ . '/includes/security.php';
require_once __DIR__ . '/includes/security_ip_rules.php'; // ✅ IP/CIDR rules
require_once __DIR__ . '/includes/security_rate_limit.php'; // ✅ rate-limit (app_settings + security_rate_limits)

if (!isset($pdo) || !($pdo instanceof PDO)) {
    $pdo = function_exists('db') ? db() : null;
}
if (!($pdo instanceof PDO)) {
    http_response_code(500);
    exit('DB error');
}

$title = t('register');
$err = null;

/* SEO: register page should not be indexed */
$meta_robots = 'noindex, nofollow, noarchive, nosnippet';
$meta_desc   = '';
$canonical   = function_exists('abs_url') ? abs_url('/register.php') : '/register.php';
if (!headers_sent()) {
    header('X-Robots-Tag: noindex, nofollow, noarchive, nosnippet', true);
}

/* ===== small i18n fallback (якщо ключів ще нема в i18n.php) ===== */
if (!function_exists('tr')) {
    function tr(string $key, array $fallbacks): string {
        $v = function_exists('t') ? (string)t($key) : $key;
        if ($v !== '' && $v !== $key) return $v;

        $l = function_exists('lang') ? (string)lang() : 'uk';
        if (!in_array($l, ['uk','ru','en'], true)) $l = 'uk';

        return (string)($fallbacks[$l] ?? $fallbacks['uk'] ?? $key);
    }
}

/* ===== DB helpers ===== */
if (!function_exists('db_has_column')) {
    function db_has_column(PDO $pdo, string $table, string $col): bool {
        try {
            $st = $pdo->prepare("SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = ? AND column_name = ? LIMIT 1");
            $st->execute([$table, $col]);
            return (bool)$st->fetchColumn();
        } catch (Throwable $e) {
            return false;
        }
    }
}
if (!function_exists('pick_first_existing_col')) {
    function pick_first_existing_col(PDO $pdo, string $table, array $candidates): ?string {
        foreach ($candidates as $c) {
            if (db_has_column($pdo, $table, (string)$c)) return (string)$c;
        }
        return null;
    }
}
if (!function_exists('abs_url_local')) {
    function abs_url_local(string $path): string {
        if (function_exists('abs_url')) return (string)abs_url($path);
        $https = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off');
        $scheme = $https ? 'https' : 'http';
        $host = (string)($_SERVER['HTTP_HOST'] ?? 'localhost');
        return $scheme . '://' . $host . $path;
    }
}
if (!function_exists('is_valid_date_ymd')) {
    function is_valid_date_ymd(string $s): bool {
        $s = trim($s);
        if (!preg_match('~^\d{4}-\d{2}-\d{2}$~', $s)) return false;
        $dt = DateTime::createFromFormat('Y-m-d', $s);
        if (!$dt) return false;
        return $dt->format('Y-m-d') === $s;
    }
}

/* ===== Rate-limit helpers (register scopes: ip / email / combined) ===== */
function rate_register_build_scopes(string $mode, string $ip, string $email): array {
    $ip = trim($ip);
    $email = mb_strtolower(trim($email), 'UTF-8');

    $scopes = [];

    if ($mode === 'ip') {
        if ($ip !== '') $scopes[] = ['type' => 'ip', 'key' => $ip];
    } elseif ($mode === 'user') {
        if ($email !== '') $scopes[] = ['type' => 'email', 'key' => $email];
    } else { // both
        if ($ip !== '' && $email !== '') {
            $scopes[] = ['type' => 'ip_email', 'key' => $ip . '|' . $email];
        }
        if ($ip !== '') $scopes[] = ['type' => 'ip', 'key' => $ip];
        if ($email !== '') $scopes[] = ['type' => 'email', 'key' => $email];
    }

    $uniq = [];
    foreach ($scopes as $s) {
        $k = (string)$s['type'] . '|' . (string)$s['key'];
        $uniq[$k] = $s;
    }
    return array_values($uniq);
}

function rate_lock_message(?string $lockedUntil): string {
    $lockedUntil = trim((string)$lockedUntil);
    if ($lockedUntil === '') {
        return tr('rate_locked_generic', [
            'uk' => 'Забагато спроб. Спробуйте пізніше.',
            'ru' => 'Слишком много попыток. Попробуйте позже.',
            'en' => 'Too many attempts. Try again later.',
        ]);
    }

    $ts = strtotime($lockedUntil);
    if ($ts <= 0) {
        return tr('rate_locked_generic', [
            'uk' => 'Забагато спроб. Спробуйте пізніше.',
            'ru' => 'Слишком много попыток. Попробуйте позже.',
            'en' => 'Too many attempts. Try again later.',
        ]);
    }

    $leftSec = $ts - time();
    if ($leftSec < 0) $leftSec = 0;
    $mins = (int)ceil($leftSec / 60);
    if ($mins < 1) $mins = 1;

    return tr('rate_locked_until', [
        'uk' => 'Забагато спроб. Спробуйте знову через ' . $mins . ' хв.',
        'ru' => 'Слишком много попыток. Попробуйте снова через ' . $mins . ' мин.',
        'en' => 'Too many attempts. Try again in ' . $mins . ' min.',
    ]);
}

/* ===== detect columns for registration & activation ===== */
$fullNameCol = pick_first_existing_col($pdo, 'users', ['full_name','name']);
$firstNameCol = db_has_column($pdo, 'users', 'first_name') ? 'first_name' : null;
$lastNameCol  = db_has_column($pdo, 'users', 'last_name') ? 'last_name' : null;

$genderCol = db_has_column($pdo, 'users', 'gender') ? 'gender' : null;
$birthCol  = pick_first_existing_col($pdo, 'users', ['birthdate','birth_date']);

$emailCol    = db_has_column($pdo, 'users', 'email') ? 'email' : null;
$usernameCol = db_has_column($pdo, 'users', 'username') ? 'username' : null;
$passCol     = db_has_column($pdo, 'users', 'pass_hash') ? 'pass_hash' : (db_has_column($pdo, 'users', 'password_hash') ? 'password_hash' : null);
$langCol     = db_has_column($pdo, 'users', 'lang') ? 'lang' : null;
$roleCol     = db_has_column($pdo, 'users', 'role') ? 'role' : null;

$createdAtCol = db_has_column($pdo, 'users', 'created_at') ? 'created_at' : null;

$activeCol = pick_first_existing_col($pdo, 'users', ['is_active','active','verified','email_verified','is_verified']);
$tokenCol  = pick_first_existing_col($pdo, 'users', ['activation_token_hash','activation_token','verify_token','verification_token','email_token','token']);
$sentAtCol = pick_first_existing_col($pdo, 'users', ['activation_sent_at','verify_sent_at','verification_sent_at']);
$activatedAtCol = pick_first_existing_col($pdo, 'users', ['activated_at','verified_at','email_verified_at']);

$hasAvatarCol = db_has_column($pdo, 'users', 'avatar_path');
$hasAboutCol  = db_has_column($pdo, 'users', 'about');
$hasCountryCodeCol = db_has_column($pdo, 'users', 'country_code');
$hasCountryCol     = db_has_column($pdo, 'users', 'country');
$hasCityCol     = db_has_column($pdo, 'users', 'city');
$hasEducationCol= db_has_column($pdo, 'users', 'education');
$hasWorkplaceCol= db_has_column($pdo, 'users', 'workplace');
$hasWorkCol     = db_has_column($pdo, 'users', 'work'); // fallback
$workCol = $hasWorkplaceCol ? 'workplace' : ($hasWorkCol ? 'work' : null);

/* ===== schema check (щоб не впасти на INSERT) ===== */
$missing = [];
foreach ([
    'email' => $emailCol,
    'username' => $usernameCol,
    'pass' => $passCol,
    'gender' => $genderCol,
    'birth' => $birthCol,
] as $k => $v) {
    if (!$v) $missing[] = $k;
}
if (!$fullNameCol && !$firstNameCol && !$lastNameCol) $missing[] = 'full_name_or_first_last';
if (!$activeCol) $missing[] = 'active_col';
if (!$tokenCol)  $missing[] = 'token_col';

if ($missing) {
    http_response_code(500);
    require __DIR__ . '/includes/header.php';
    ?>
    <div class="container my-4">
      <div class="alert alert-danger">
        <div class="fw-bold mb-1">DB schema missing</div>
        <div class="mb-2">Table <code>users</code> does not support registration fields: <code><?= h(implode(', ', $missing)) ?></code></div>
        <div class="small text-muted">Додай потрібні колонки або дай мені дамп структури users — я складу міграцію.</div>
      </div>
    </div>
    <?php
    require __DIR__ . '/includes/footer.php';
    exit;
}

/**
 * Повний список країн (ISO 3166-1 alpha-2) + English назва.
 */
$COUNTRIES = [
  'AF'=>'Afghanistan','AL'=>'Albania','DZ'=>'Algeria','AS'=>'American Samoa','AD'=>'Andorra','AO'=>'Angola','AI'=>'Anguilla','AQ'=>'Antarctica','AG'=>'Antigua and Barbuda','AR'=>'Argentina','AM'=>'Armenia','AW'=>'Aruba','AU'=>'Australia','AT'=>'Austria','AZ'=>'Azerbaijan',
  'BS'=>'Bahamas','BH'=>'Bahrain','BD'=>'Bangladesh','BB'=>'Barbados','BY'=>'Belarus','BE'=>'Belgium','BZ'=>'Belize','BJ'=>'Benin','BM'=>'Bermuda','BT'=>'Bhutan','BO'=>'Bolivia','BA'=>'Bosnia and Herzegovina','BW'=>'Botswana','BV'=>'Bouvet Island','BR'=>'Brazil','IO'=>'British Indian Ocean Territory','BN'=>'Brunei','BG'=>'Bulgaria','BF'=>'Burkina Faso','BI'=>'Burundi',
  'KH'=>'Cambodia','CM'=>'Cameroon','CA'=>'Canada','CV'=>'Cape Verde','KY'=>'Cayman Islands','CF'=>'Central African Republic','TD'=>'Chad','CL'=>'Chile','CN'=>'China','CX'=>'Christmas Island','CC'=>'Cocos (Keeling) Islands','CO'=>'Colombia','KM'=>'Comoros','CG'=>'Congo','CD'=>'Congo (DRC)','CK'=>'Cook Islands','CR'=>'Costa Rica','CI'=>"Côte d’Ivoire",'HR'=>'Croatia','CU'=>'Cuba','CY'=>'Cyprus','CZ'=>'Czechia',
  'DK'=>'Denmark','DJ'=>'Djibouti','DM'=>'Dominica','DO'=>'Dominican Republic',
  'EC'=>'Ecuador','EG'=>'Egypt','SV'=>'El Salvador','GQ'=>'Equatorial Guinea','ER'=>'Eritrea','EE'=>'Estonia','SZ'=>'Eswatini','ET'=>'Ethiopia',
  'FK'=>'Falkland Islands','FO'=>'Faroe Islands','FJ'=>'Fiji','FI'=>'Finland','FR'=>'France','GF'=>'French Guiana','PF'=>'French Polynesia','TF'=>'French Southern Territories',
  'GA'=>'Gabon','GM'=>'Gambia','GE'=>'Georgia','DE'=>'Germany','GH'=>'Ghana','GI'=>'Gibraltar','GR'=>'Greece','GL'=>'Greenland','GD'=>'Grenada','GP'=>'Guadeloupe','GU'=>'Guam','GT'=>'Guatemala','GG'=>'Guernsey','GN'=>'Guinea','GW'=>'Guinea-Bissau','GY'=>'Guyana',
  'HT'=>'Haiti','HM'=>'Heard Island and McDonald Islands','VA'=>'Vatican City','HN'=>'Honduras','HK'=>'Hong Kong','HU'=>'Hungary',
  'IS'=>'Iceland','IN'=>'India','ID'=>'Indonesia','IR'=>'Iran','IQ'=>'Iraq','IE'=>'Ireland','IM'=>'Isle of Man','IL'=>'Israel','IT'=>'Italy',
  'JM'=>'Jamaica','JP'=>'Japan','JE'=>'Jersey','JO'=>'Jordan',
  'KZ'=>'Kazakhstan','KE'=>'Kenya','KI'=>'Kiribati','KP'=>'North Korea','KR'=>'South Korea','KW'=>'Kuwait','KG'=>'Kyrgyzstan',
  'LA'=>'Laos','LV'=>'Latvia','LB'=>'Lebanon','LS'=>'Lesotho','LR'=>'Liberia','LY'=>'Libya','LI'=>'Liechtenstein','LT'=>'Lithuania','LU'=>'Luxembourg',
  'MO'=>'Macao','MG'=>'Madagascar','MW'=>'Malawi','MY'=>'Malaysia','MV'=>'Maldives','ML'=>'Mali','MT'=>'Malta','MH'=>'Marshall Islands','MQ'=>'Martinique','MR'=>'Mauritania','MU'=>'Mauritius','YT'=>'Mayotte','MX'=>'Mexico','FM'=>'Micronesia','MD'=>'Moldova','MC'=>'Monaco','MN'=>'Mongolia','ME'=>'Montenegro','MS'=>'Montserrat','MA'=>'Morocco','MZ'=>'Mozambique','MM'=>'Myanmar',
  'NA'=>'Namibia','NR'=>'Nauru','NP'=>'Nepal','NL'=>'Netherlands','NC'=>'New Caledonia','NZ'=>'New Zealand','NI'=>'Nicaragua','NE'=>'Niger','NG'=>'Nigeria','NU'=>'Niue','NF'=>'Norfolk Island','MK'=>'North Macedonia','MP'=>'Northern Mariana Islands','NO'=>'Norway',
  'OM'=>'Oman',
  'PK'=>'Pakistan','PW'=>'Palau','PS'=>'Palestine','PA'=>'Panama','PG'=>'Papua New Guinea','PY'=>'Paraguay','PE'=>'Peru','PH'=>'Philippines','PN'=>'Pitcairn Islands','PL'=>'Poland','PT'=>'Portugal','PR'=>'Puerto Rico',
  'QA'=>'Qatar',
  'RE'=>'Réunion','RO'=>'Romania','RU'=>'Russia','RW'=>'Rwanda',
  'BL'=>'Saint Barthélemy','SH'=>'Saint Helena','KN'=>'Saint Kitts and Nevis','LC'=>'Saint Lucia','MF'=>'Saint Martin','PM'=>'Saint Pierre and Miquelon','VC'=>'Saint Vincent and the Grenadines','WS'=>'Samoa','SM'=>'San Marino','ST'=>'São Tomé and Príncipe','SA'=>'Saudi Arabia','SN'=>'Senegal','RS'=>'Serbia','SC'=>'Seychelles','SL'=>'Sierra Leone','SG'=>'Singapore','SX'=>'Sint Maarten','SK'=>'Slovakia','SI'=>'Slovenia','SB'=>'Solomon Islands','SO'=>'Somalia','ZA'=>'South Africa','GS'=>'South Georgia and the South Sandwich Islands','SS'=>'South Sudan','ES'=>'Spain','LK'=>'Sri Lanka','SD'=>'Sudan','SR'=>'Suriname','SJ'=>'Svalbard and Jan Mayen','SE'=>'Sweden','CH'=>'Switzerland','SY'=>'Syria',
  'TW'=>'Taiwan','TJ'=>'Tajikistan','TZ'=>'Tanzania','TH'=>'Thailand','TL'=>'Timor-Leste','TG'=>'Togo','TK'=>'Tokelau','TO'=>'Tonga','TT'=>'Trinidad and Tobago','TN'=>'Tunisia','TR'=>'Turkey','TM'=>'Turkmenistan','TC'=>'Turks and Caicos Islands','TV'=>'Tuvalu',
  'UG'=>'Uganda','UA'=>'Ukraine','AE'=>'United Arab Emirates','GB'=>'United Kingdom','US'=>'United States','UM'=>'U.S. Minor Outlying Islands','UY'=>'Uruguay','UZ'=>'Uzbekistan',
  'VU'=>'Vanuatu','VE'=>'Venezuela','VN'=>'Vietnam','VG'=>'British Virgin Islands','VI'=>'U.S. Virgin Islands',
  'WF'=>'Wallis and Futuna',
  'EH'=>'Western Sahara',
  'YE'=>'Yemen',
  'ZM'=>'Zambia','ZW'=>'Zimbabwe',
];

function country_ui_label(string $code, string $lang, array $COUNTRIES): string {
    $code = strtoupper(trim($code));
    return (string)($COUNTRIES[$code] ?? $code);
}
function country_canonical_en(string $code, array $COUNTRIES): string {
    $code = strtoupper(trim($code));
    return (string)($COUNTRIES[$code] ?? '');
}

/* ===== avatar upload helper ===== */
function save_avatar_upload(string $username, array $file): array
{
    if (!isset($file['error']) || (int)$file['error'] === UPLOAD_ERR_NO_FILE) {
        return ['ok' => true, 'path' => null, 'err' => null];
    }
    if ((int)$file['error'] !== UPLOAD_ERR_OK) {
        return ['ok' => false, 'path' => null, 'err' => tr('upload_fail', [
            'uk' => 'Помилка завантаження файлу.',
            'ru' => 'Ошибка загрузки файла.',
            'en' => 'File upload error.',
        ])];
    }

    $maxBytes = 5 * 1024 * 1024; // 5MB
    if (!isset($file['size']) || (int)$file['size'] > $maxBytes) {
        return ['ok' => false, 'path' => null, 'err' => tr('avatar_too_big', [
            'uk' => 'Аватарка занадто велика (макс. 5MB).',
            'ru' => 'Аватар слишком большой (макс. 5MB).',
            'en' => 'Avatar is too large (max 5MB).',
        ])];
    }

    $tmp = (string)($file['tmp_name'] ?? '');
    if ($tmp === '' || !is_uploaded_file($tmp)) {
        return ['ok' => false, 'path' => null, 'err' => tr('upload_fail', [
            'uk' => 'Помилка завантаження файлу.',
            'ru' => 'Ошибка загрузки файла.',
            'en' => 'File upload error.',
        ])];
    }

    $type = @exif_imagetype($tmp);
    $ext = match ($type) {
        IMAGETYPE_JPEG => 'jpg',
        IMAGETYPE_PNG  => 'png',
        IMAGETYPE_GIF  => 'gif',
        defined('IMAGETYPE_WEBP') ? IMAGETYPE_WEBP : -1 => 'webp',
        default => '',
    };

    if ($ext === '') {
        return ['ok' => false, 'path' => null, 'err' => tr('avatar_bad_type', [
            'uk' => 'Невірний формат аватарки. Дозволено: JPG/PNG/GIF/WEBP.',
            'ru' => 'Неверный формат аватара. Разрешено: JPG/PNG/GIF/WEBP.',
            'en' => 'Invalid avatar format. Allowed: JPG/PNG/GIF/WEBP.',
        ])];
    }

    if (!preg_match('/^[a-zA-Z0-9._-]{3,32}$/', $username)) {
        return ['ok' => false, 'path' => null, 'err' => tr('username_bad', [
            'uk' => 'Нікнейм може містити лише латиницю, цифри та символи . _ - (3–32).',
            'ru' => 'Никнейм может содержать только латиницу, цифры и символы . _ - (3–32).',
            'en' => 'Username may contain only latin letters, digits and . _ - (3–32).',
        ])];
    }

    $dirFs = __DIR__ . '/upload/' . $username . '/avatar';
    if (!is_dir($dirFs)) {
        if (!@mkdir($dirFs, 0775, true) && !is_dir($dirFs)) {
            return ['ok' => false, 'path' => null, 'err' => tr('avatar_dir_fail', [
                'uk' => 'Не вдалося створити папку для аватарки.',
                'ru' => 'Не удалось создать папку для аватара.',
                'en' => 'Failed to create avatar folder.',
            ])];
        }
    }

    $name = date('Ymd_His_') . bin2hex(random_bytes(6)) . '.' . $ext;
    $dstFs = $dirFs . '/' . $name;

    if (!@move_uploaded_file($tmp, $dstFs)) {
        return ['ok' => false, 'path' => null, 'err' => tr('upload_fail', [
            'uk' => 'Помилка завантаження файлу.',
            'ru' => 'Ошибка загрузки файла.',
            'en' => 'File upload error.',
        ])];
    }

    @chmod($dstFs, 0644);

    $pathWeb = '/upload/' . $username . '/avatar/' . $name;
    return ['ok' => true, 'path' => $pathWeb, 'err' => null];
}

/* =========================
   POST
========================= */
if (is_post()) {

    $clientIp = (string)($_SERVER['REMOTE_ADDR'] ?? '');
    $emailForScope = '';
    try { $emailForScope = mb_strtolower(trim((string)($_POST['email'] ?? '')), 'UTF-8'); } catch (Throwable $e) { $emailForScope = ''; }

    /* ✅ RATE LIMIT: check locked (register) */
    $rateSettings = null;
    $rateScopes = [];
    try {
        $rateSettings = security_rate_get_settings($pdo);
        $mode = (string)($rateSettings['mode'] ?? 'both');
        if (!in_array($mode, ['ip','user','both'], true)) $mode = 'both';

        $rateScopes = rate_register_build_scopes($mode, $clientIp, $emailForScope);

        $lockedInfo = security_rate_is_locked($pdo, $rateScopes);
        if (!empty($lockedInfo['locked'])) {
            try { security_log_login_attempt($pdo, false, 'rate_locked_register', null, $emailForScope); } catch (Throwable $e) {}
            flash_set('err', rate_lock_message((string)($lockedInfo['locked_until'] ?? '')));
            redirect('/register.php');
        }
    } catch (Throwable $e) {
        $rateSettings = null;
        $rateScopes = [];
    }

    /* ✅ BLOCKED IP CHECK (register) — show reason to user */
    try {
        if (function_exists('security_ip_is_blocked')) {
            $chk = security_ip_is_blocked($pdo, $clientIp, 'register');
            if (!empty($chk['blocked'])) {

                try { security_log_login_attempt($pdo, false, 'ip_blocked_register', null, $emailForScope); } catch (Throwable $e) {}

                try {
                    if (is_array($rateSettings) && $rateScopes) {
                        security_rate_register_failure($pdo, $rateSettings, $rateScopes);
                    }
                } catch (Throwable $e) {}

                $reason = trim(strip_tags((string)($chk['reason'] ?? '')));
                if ($reason === '') $reason = 'Blocked';

                flash_set('err', tr('ip_blocked_msg', [
                    'uk' => 'Вас заблоковано: ' . $reason,
                    'ru' => 'Вы заблокированы: ' . $reason,
                    'en' => 'You are blocked: ' . $reason,
                ]));
                redirect('/register.php');
            }
        }
    } catch (Throwable $e) {}

    if (!csrf_check(post_str('csrf'))) {
        try { security_log_login_attempt($pdo, false, 'register_csrf', null, $emailForScope); } catch (Throwable $e) {}

        try {
            if (is_array($rateSettings) && $rateScopes) {
                security_rate_register_failure($pdo, $rateSettings, $rateScopes);
            }
        } catch (Throwable $e) {}

        flash_set('err', t('bad_csrf'));
        redirect('/register.php');
    }

    $email    = mb_strtolower(post_str('email'));
    $username = trim(post_str('username'));
    $pass1    = post_str('password');
    $pass2    = post_str('password2');

    $fullName = trim(post_str('full_name'));
    $gender   = trim(post_str('gender'));      // f | m
    $bdate    = trim(post_str('birthdate'));   // YYYY-MM-DD

    $about     = trim(post_str('about'));

    // ✅ країна з select (код)
    $countryCode = strtoupper(trim(post_str('country_code')));
    if ($countryCode === '') {
        $countryLegacy = trim(post_str('country'));
        if (preg_match('/^[A-Za-z]{2}$/', $countryLegacy)) $countryCode = strtoupper($countryLegacy);
    }

    $city      = trim(post_str('city'));
    $education = trim(post_str('education'));
    $workplace = trim(post_str('workplace'));

    /* ===== validate required ===== */
    if ($email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
        try { security_log_login_attempt($pdo, false, 'register_bad_email', null, $email); } catch (Throwable $e) {}
        $err = tr('bad_email', ['uk'=>'Невірний email.', 'ru'=>'Неверный email.', 'en'=>'Invalid email.']);
    } elseif ($username === '') {
        try { security_log_login_attempt($pdo, false, 'register_bad_username', null, $email); } catch (Throwable $e) {}
        $err = tr('bad_username', ['uk'=>'Вкажи нікнейм.', 'ru'=>'Укажи никнейм.', 'en'=>'Enter username.']);
    } elseif (!preg_match('/^[a-zA-Z0-9._-]{3,32}$/', $username)) {
        try { security_log_login_attempt($pdo, false, 'register_username_format', null, $email); } catch (Throwable $e) {}
        $err = tr('username_bad', [
            'uk' => 'Нікнейм може містити лише латиницю, цифри та символи . _ - (3–32).',
            'ru' => 'Никнейм может содержать только латиницу, цифры и символы . _ - (3–32).',
            'en' => 'Username may contain only latin letters, digits and . _ - (3–32).',
        ]);
    } elseif (mb_strlen($pass1) < 6) {
        try { security_log_login_attempt($pdo, false, 'register_password_short', null, $email); } catch (Throwable $e) {}
        $err = t('password_short');
    } elseif ($pass1 !== $pass2) {
        try { security_log_login_attempt($pdo, false, 'register_password_mismatch', null, $email); } catch (Throwable $e) {}
        $err = t('password_mismatch');
    } elseif ($fullNameCol && $fullName === '' && !$firstNameCol && !$lastNameCol) {
        try { security_log_login_attempt($pdo, false, 'register_fullname_required', null, $email); } catch (Throwable $e) {}
        $err = tr('full_name_required', ['uk'=>'Вкажи ПІБ.', 'ru'=>'Укажи ФИО.', 'en'=>'Enter full name.']);
    } elseif (!in_array($gender, ['f','m'], true)) {
        try { security_log_login_attempt($pdo, false, 'register_gender_required', null, $email); } catch (Throwable $e) {}
        $err = tr('gender_required', ['uk'=>'Обери стать.', 'ru'=>'Выбери пол.', 'en'=>'Select gender.']);
    } elseif (!is_valid_date_ymd($bdate)) {
        try { security_log_login_attempt($pdo, false, 'register_birthdate_bad', null, $email); } catch (Throwable $e) {}
        $err = tr('birthdate_bad', ['uk'=>'Невірна дата народження.', 'ru'=>'Неверная дата рождения.', 'en'=>'Invalid birth date.']);
    } else {
        $dt = DateTime::createFromFormat('Y-m-d', $bdate);
        $today = new DateTime('today');
        if ($dt && $dt > $today) {
            try { security_log_login_attempt($pdo, false, 'register_birthdate_future', null, $email); } catch (Throwable $e) {}
            $err = tr('birthdate_future', ['uk'=>'Дата народження не може бути в майбутньому.', 'ru'=>'Дата рождения не может быть в будущем.', 'en'=>'Birth date cannot be in the future.']);
        }
    }

    // ✅ валідація країни (якщо вибрано)
    if (!$err && $countryCode !== '' && !isset($COUNTRIES[$countryCode])) {
        try { security_log_login_attempt($pdo, false, 'register_country_bad', null, $email); } catch (Throwable $e) {}
        $err = tr('country_bad', [
            'uk' => 'Обери країну зі списку.',
            'ru' => 'Выбери страну из списка.',
            'en' => 'Select a country from the list.',
        ]);
    }

    if (!$err) {
        $st = $pdo->prepare("SELECT id FROM users WHERE email=? LIMIT 1");
        $st->execute([$email]);
        if ($st->fetchColumn()) {
            try { security_log_login_attempt($pdo, false, 'register_email_taken', null, $email); } catch (Throwable $e) {}
            $err = t('email_taken');
        } else {
            $st = $pdo->prepare("SELECT id FROM users WHERE username=? LIMIT 1");
            $st->execute([$username]);
            if ($st->fetchColumn()) {
                try { security_log_login_attempt($pdo, false, 'register_username_taken', null, $email); } catch (Throwable $e) {}
                $err = t('username_taken');
            }
        }
    }

    /* ===== avatar upload (optional) ===== */
    $avatarPath = null;
    if (!$err && $hasAvatarCol) {
        $up = save_avatar_upload($username, $_FILES['avatar'] ?? []);
        if (!$up['ok']) {
            try { security_log_login_attempt($pdo, false, 'register_avatar_fail', null, $email); } catch (Throwable $e) {}
            $err = (string)$up['err'];
        } else {
            $avatarPath = $up['path'];
        }
    }

    // ✅ Rate-limit: register failure on any validation error
    if ($err) {
        try {
            if (is_array($rateSettings) && $rateScopes) {
                security_rate_register_failure($pdo, $rateSettings, $rateScopes);
            }
        } catch (Throwable $e) {}

        flash_set('err', $err);
        redirect('/register.php');
    }

    /* ===== insert + mail ===== */
    try {
        $hash = password_hash($pass1, PASSWORD_DEFAULT);

        // activation token
        $rawToken = bin2hex(random_bytes(32));
        $isHashCol = (bool)preg_match('~(_hash|hash)$~i', (string)$tokenCol);
        $storeToken = $isHashCol ? hash('sha256', $rawToken) : $rawToken;

        $userLang = function_exists('lang') ? (string)lang() : 'uk';
        if (!in_array($userLang, ['uk','ru','en'], true)) $userLang = 'uk';

        $cols = [];
        $vals = [];
        $params = [];

        // required core
        $cols[] = $emailCol;    $vals[] = ':email';     $params[':email'] = $email;
        $cols[] = $usernameCol; $vals[] = ':username';  $params[':username'] = $username;
        $cols[] = $passCol;     $vals[] = ':pass_hash'; $params[':pass_hash'] = $hash;

        if ($langCol) { $cols[] = $langCol; $vals[] = ':lang'; $params[':lang'] = $userLang; }
        if ($roleCol) { $cols[] = $roleCol; $vals[] = ':role'; $params[':role'] = 'user'; }

        // profile required
        if ($fullNameCol) { $cols[] = $fullNameCol; $vals[] = ':full_name'; $params[':full_name'] = $fullName; }

        // якщо є first/last — заповнюємо з full_name best-effort
        if (($firstNameCol || $lastNameCol) && $fullName !== '') {
            $parts = preg_split('~\s+~u', $fullName, -1, PREG_SPLIT_NO_EMPTY) ?: [];
            $first = $parts ? (string)array_shift($parts) : '';
            $last  = $parts ? trim(implode(' ', $parts)) : '';
            if ($firstNameCol) { $cols[] = $firstNameCol; $vals[] = ':first_name'; $params[':first_name'] = ($first !== '' ? $first : null); }
            if ($lastNameCol)  { $cols[] = $lastNameCol;  $vals[] = ':last_name';  $params[':last_name']  = ($last  !== '' ? $last  : null); }
        }

        $cols[] = $genderCol; $vals[] = ':gender'; $params[':gender'] = $gender;
        $cols[] = $birthCol;  $vals[] = ':birth';  $params[':birth']  = $bdate;

        // activation fields
        $cols[] = $tokenCol; $vals[] = ':token'; $params[':token'] = $storeToken;
        if ($sentAtCol) { $cols[] = $sentAtCol; $vals[] = 'NOW()'; }
        if ($activeCol) { $cols[] = $activeCol; $vals[] = '0'; }
        if ($activatedAtCol) { $cols[] = $activatedAtCol; $vals[] = 'NULL'; }

        if ($createdAtCol) { $cols[] = $createdAtCol; $vals[] = 'NOW()'; }

        // optional profile
        if ($hasAvatarCol) { $cols[] = 'avatar_path'; $vals[] = ':avatar_path'; $params[':avatar_path'] = $avatarPath; }
        if ($hasAboutCol)  { $cols[] = 'about';       $vals[] = ':about';       $params[':about'] = ($about === '' ? null : $about); }

        $countryCanonicalEn = ($countryCode !== '' ? country_canonical_en($countryCode, $COUNTRIES) : null);
        if ($hasCountryCodeCol) { $cols[] = 'country_code'; $vals[] = ':country_code'; $params[':country_code'] = ($countryCode === '' ? null : $countryCode); }
        if ($hasCountryCol)     { $cols[] = 'country';      $vals[] = ':country';      $params[':country'] = ($countryCanonicalEn === '' ? null : $countryCanonicalEn); }

        if ($hasCityCol)      { $cols[] = 'city';      $vals[] = ':city';      $params[':city'] = ($city === '' ? null : $city); }
        if ($hasEducationCol) { $cols[] = 'education'; $vals[] = ':education'; $params[':education'] = ($education === '' ? null : $education); }
        if ($workCol)         { $cols[] = $workCol;    $vals[] = ':workplace'; $params[':workplace'] = ($workplace === '' ? null : $workplace); }

        $sql = "INSERT INTO users (" . implode(',', $cols) . ") VALUES (" . implode(',', $vals) . ")";
        $st = $pdo->prepare($sql);
        $st->execute($params);

        $newUserId = (int)$pdo->lastInsertId();

        $activateLink = abs_url_local('/activate.php?token=' . urlencode($rawToken));
        $subj = t('activation_subject');

        $html = '
        <div style="font-family:Arial,sans-serif;line-height:1.5;color:#111">
          <h2 style="margin:0 0 12px 0;">' . h(t('activation_mail_title')) . '</h2>
          <p style="margin:0 0 14px 0;">' . h(t('activation_mail_text')) . '</p>
          <p style="margin:18px 0;">
            <a href="' . h($activateLink) . '" style="display:inline-block;padding:12px 18px;border-radius:10px;background:#1f3a5f;color:#fff;text-decoration:none;">
              ' . h(t('activation_button')) . '
            </a>
          </p>
          <p style="margin:18px 0 6px 0;color:#555;font-size:13px;">' . h(t('activation_link_fallback')) . '</p>
          <div style="font-size:13px;">
            <a href="' . h($activateLink) . '">' . h($activateLink) . '</a>
          </div>
        </div>';

        $text = t('activation_mail_title') . "\n\n" .
                t('activation_mail_text') . "\n" .
                $activateLink . "\n";

        $send = ['ok' => false, 'error' => 'mailer_send not found'];
        if (function_exists('mailer_send')) {
            $send = (array)mailer_send($email, ($fullName !== '' ? $fullName : $username), $subj, $html, $text);
        }

        if (!($send['ok'] ?? false)) {
            try { security_log_login_attempt($pdo, false, 'register_mail_fail', $newUserId > 0 ? $newUserId : null, $email); } catch (Throwable $e) {}

            try {
                if (is_array($rateSettings) && $rateScopes) {
                    security_rate_register_failure($pdo, $rateSettings, $rateScopes);
                }
            } catch (Throwable $e) {}

            flash_set('err', t('mail_fail'));
            redirect('/login.php');
        }

        // ✅ SUCCESS: clear limiter for ip/email
        try {
            if ($rateScopes) {
                security_rate_clear($pdo, $rateScopes);
            }
        } catch (Throwable $e) {}

        try { security_log_login_attempt($pdo, true, 'register_ok', $newUserId > 0 ? $newUserId : null, $email); } catch (Throwable $e) {}

        flash_set('ok', t('activation_sent'));
        redirect('/login.php');
    } catch (Throwable $e) {
        try { security_log_login_attempt($pdo, false, 'register_db_fail', null, $email); } catch (Throwable $e2) {}

        try {
            if (is_array($rateSettings) && $rateScopes) {
                security_rate_register_failure($pdo, $rateSettings, $rateScopes);
            }
        } catch (Throwable $e3) {}

        flash_set('err', 'DB error');
        redirect('/register.php');
    }
}

require __DIR__ . '/includes/header.php';

$l = function_exists('lang') ? (string)lang() : 'uk';
if (!in_array($l, ['uk','ru','en'], true)) $l = 'uk';
?>

<div class="row justify-content-center">
  <div class="col-md-8 col-lg-7">
    <div class="card card-app p-4">
      <h1 class="h4 mb-3"><?= h(t('register')) ?></h1>

      <form method="post" class="vstack gap-3" enctype="multipart/form-data">
        <?= csrf_field() ?>

        <!-- REQUIRED -->
        <div class="row g-3">
          <div class="col-md-6">
            <label class="form-label"><?= h(t('email')) ?></label>
            <input class="form-control" type="email" name="email" required autocomplete="email">
          </div>

          <div class="col-md-6">
            <label class="form-label"><?= h(t('username')) ?></label>
            <input class="form-control" type="text" name="username" required autocomplete="username" placeholder="demonx">
            <div class="form-text">
              <?= h(tr('username_hint', [
                'uk' => 'Латиниця/цифри та . _ - (3–32).',
                'ru' => 'Латиница/цифры и . _ - (3–32).',
                'en' => 'Latin/digits and . _ - (3–32).',
              ])) ?>
            </div>
          </div>

          <div class="col-md-6">
            <label class="form-label"><?= h(t('password')) ?></label>
            <input class="form-control" type="password" name="password" required autocomplete="new-password">
          </div>

          <div class="col-md-6">
            <label class="form-label"><?= h(t('repeat_password')) ?></label>
            <input class="form-control" type="password" name="password2" required autocomplete="new-password">
          </div>

          <div class="col-md-12">
            <label class="form-label"><?= h(tr('full_name', ['uk'=>'ПІБ', 'ru'=>'ФИО', 'en'=>'Full name'])) ?></label>
            <input class="form-control" type="text" name="full_name" required autocomplete="name">
          </div>

          <div class="col-md-6">
            <label class="form-label"><?= h(tr('gender', ['uk'=>'Стать', 'ru'=>'Пол', 'en'=>'Gender'])) ?></label>
            <select class="form-select" name="gender" required>
              <option value="" selected disabled><?= h(tr('select', ['uk'=>'Обрати...', 'ru'=>'Выбрать...', 'en'=>'Select...'])) ?></option>
              <option value="f"><?= h(tr('gender_female', ['uk'=>'Жіноча', 'ru'=>'Женский', 'en'=>'Female'])) ?></option>
              <option value="m"><?= h(tr('gender_male', ['uk'=>'Чоловіча', 'ru'=>'Мужской', 'en'=>'Male'])) ?></option>
            </select>
          </div>

          <div class="col-md-6">
            <label class="form-label"><?= h(tr('birthdate', ['uk'=>'Дата народження', 'ru'=>'Дата рождения', 'en'=>'Birth date'])) ?></label>
            <input class="form-control" type="date" name="birthdate" required>
          </div>
        </div>

        <hr class="my-2">

        <!-- OPTIONAL -->
        <div class="row g-3">
          <?php if ($hasAvatarCol): ?>
          <div class="col-md-6">
            <label class="form-label"><?= h(tr('avatar', ['uk'=>'Аватарка', 'ru'=>'Аватар', 'en'=>'Avatar'])) ?></label>
            <input class="form-control" type="file" name="avatar" accept="image/*">
            <div class="form-text">
              <?= h(tr('avatar_hint', [
                'uk' => 'Необов’язково. JPG/PNG/GIF/WEBP до 5MB.',
                'ru' => 'Необязательно. JPG/PNG/GIF/WEBP до 5MB.',
                'en' => 'Optional. JPG/PNG/GIF/WEBP up to 5MB.',
              ])) ?>
            </div>
          </div>
          <?php endif; ?>

          <?php if ($hasCountryCodeCol || $hasCountryCol): ?>
          <div class="col-md-6">
            <label class="form-label"><?= h(tr('country', ['uk'=>'Країна', 'ru'=>'Страна', 'en'=>'Country'])) ?></label>
            <select class="form-select" name="country_code">
              <option value=""><?= h(tr('select_country_opt', [
                'uk' => 'Не вказувати',
                'ru' => 'Не указывать',
                'en' => 'Not specified',
              ])) ?></option>
              <?php foreach ($COUNTRIES as $cc => $nameEn): ?>
                <?php $cc = strtoupper((string)$cc); ?>
                <option value="<?= h($cc) ?>">
                  <?= h(country_ui_label($cc, $l, $COUNTRIES)) ?> (<?= h($cc) ?>)
                </option>
              <?php endforeach; ?>
            </select>
            <div class="form-text">
              <?= h(tr('country_hint', [
                'uk' => 'Країна обирається зі списку, щоб не було плутанини (USA/United States).',
                'ru' => 'Страна выбирается из списка, чтобы не было путаницы (USA/United States).',
                'en' => 'Choose from the list to avoid duplicates (USA/United States).',
              ])) ?>
            </div>
          </div>
          <?php endif; ?>

          <?php if ($hasCityCol): ?>
          <div class="col-md-6">
            <label class="form-label"><?= h(tr('city', ['uk'=>'Місто', 'ru'=>'Город', 'en'=>'City'])) ?></label>
            <input class="form-control" type="text" name="city">
          </div>
          <?php endif; ?>

          <?php if ($hasEducationCol): ?>
          <div class="col-md-6">
            <label class="form-label"><?= h(tr('education', ['uk'=>'Освіта', 'ru'=>'Образование', 'en'=>'Education'])) ?></label>
            <input class="form-control" type="text" name="education">
          </div>
          <?php endif; ?>

          <?php if ($workCol): ?>
          <div class="col-md-12">
            <label class="form-label"><?= h(tr('workplace', ['uk'=>'Місце роботи', 'ru'=>'Место работы', 'en'=>'Workplace'])) ?></label>
            <input class="form-control" type="text" name="workplace">
          </div>
          <?php endif; ?>

          <?php if ($hasAboutCol): ?>
          <div class="col-md-12">
            <label class="form-label"><?= h(tr('about', ['uk'=>'Про себе', 'ru'=>'О себе', 'en'=>'About'])) ?></label>
            <textarea class="form-control" name="about" rows="3"></textarea>
          </div>
          <?php endif; ?>
        </div>

        <button class="btn btn-primary w-100" type="submit"><?= h(t('register')) ?></button>

        <div class="text-muted small">
          <?= h(t('have_account')) ?> <a href="/login.php"><?= h(t('login')) ?></a>
        </div>
      </form>
    </div>
  </div>
</div>

<?php require __DIR__ . '/includes/footer.php'; ?>