<?php
/**
 * =========================================================
 * MULLERWALLET - Core Helper Functions
 * =========================================================
 * Requires: config/db.php, config/mail.php already loaded
 * =========================================================
 */

// ---------------------------------------------------------
// OTP GENERATION
// ---------------------------------------------------------

/**
 * Generate a random numeric OTP code (default 6 digits).
 */
function generateOtpCode(int $length = OTP_LENGTH): string
{
    $min = (int) str_pad('1', $length, '0');
    $max = (int) str_pad('', $length, '9');
    return (string) random_int($min, $max);
}

/**
 * Create a new OTP row for a user + purpose, invalidating any
 * previous unused OTPs for that same purpose first.
 *
 * @param PDO    $pdo
 * @param int    $userId
 * @param string $purpose  'register' | 'login' | 'reset_password'
 * @return string the generated OTP code
 */
function createOtp(PDO $pdo, int $userId, string $purpose): string
{
    // Invalidate old unused OTPs for this user + purpose
    $stmt = $pdo->prepare(
        "UPDATE otps SET is_used = 1
         WHERE user_id = :user_id AND purpose = :purpose AND is_used = 0"
    );
    $stmt->execute([':user_id' => $userId, ':purpose' => $purpose]);

    $code      = generateOtpCode();
    $expiresAt = date('Y-m-d H:i:s', strtotime('+' . OTP_EXPIRY_MINUTES . ' minutes'));

    $stmt = $pdo->prepare(
        "INSERT INTO otps (user_id, otp_code, purpose, expires_at)
         VALUES (:user_id, :otp_code, :purpose, :expires_at)"
    );
    $stmt->execute([
        ':user_id'    => $userId,
        ':otp_code'   => $code,
        ':purpose'    => $purpose,
        ':expires_at' => $expiresAt,
    ]);

    return $code;
}

/**
 * Verify a submitted OTP code for a given user + purpose.
 *
 * @return array ['success' => bool, 'message' => string]
 */
function verifyOtp(PDO $pdo, int $userId, string $purpose, string $submittedCode): array
{
    $stmt = $pdo->prepare(
        "SELECT * FROM otps
         WHERE user_id = :user_id AND purpose = :purpose AND is_used = 0
         ORDER BY id DESC LIMIT 1"
    );
    $stmt->execute([':user_id' => $userId, ':purpose' => $purpose]);
    $otp = $stmt->fetch();

    if (!$otp) {
        return ['success' => false, 'message' => 'No active OTP found. Please request a new one.'];
    }

    if (strtotime($otp['expires_at']) < time()) {
        return ['success' => false, 'message' => 'This OTP has expired. Please request a new one.'];
    }

    if ($otp['attempts'] >= OTP_MAX_ATTEMPTS) {
        return ['success' => false, 'message' => 'Too many failed attempts. Please request a new OTP.'];
    }

    if (!hash_equals($otp['otp_code'], $submittedCode)) {
        // increment failed attempts
        $pdo->prepare("UPDATE otps SET attempts = attempts + 1 WHERE id = :id")
            ->execute([':id' => $otp['id']]);

        return ['success' => false, 'message' => 'Incorrect OTP. Please try again.'];
    }

    // Mark as used
    $pdo->prepare("UPDATE otps SET is_used = 1 WHERE id = :id")
        ->execute([':id' => $otp['id']]);

    return ['success' => true, 'message' => 'OTP verified successfully.'];
}

/**
 * Check whether a user can request a new OTP yet (resend cooldown).
 *
 * @return int seconds remaining before they can resend (0 = allowed now)
 */
function otpResendSecondsLeft(PDO $pdo, int $userId, string $purpose): int
{
    $stmt = $pdo->prepare(
        "SELECT created_at FROM otps
         WHERE user_id = :user_id AND purpose = :purpose
         ORDER BY id DESC LIMIT 1"
    );
    $stmt->execute([':user_id' => $userId, ':purpose' => $purpose]);
    $last = $stmt->fetchColumn();

    if (!$last) {
        return 0;
    }

    $elapsed = time() - strtotime($last);
    $remaining = OTP_RESEND_COOLDOWN - $elapsed;

    return $remaining > 0 ? $remaining : 0;
}

// ---------------------------------------------------------
// EMAIL TEMPLATES
// ---------------------------------------------------------

/**
 * Build and send an OTP email to the user.
 */
function sendOtpEmail(string $toEmail, string $toName, string $otpCode, string $purpose): bool
{
    $subjectMap = [
        'register'       => 'Verify your Mullerwallet account',
        'login'          => 'Your Mullerwallet login code',
        'reset_password' => 'Reset your Mullerwallet password',
    ];
    $subject = $subjectMap[$purpose] ?? 'Your Mullerwallet OTP code';

    $htmlBody = '
    <div style="font-family: Arial, sans-serif; background:#f4f4f4; padding:30px;">
        <div style="max-width:480px; margin:auto; background:#ffffff; border-radius:10px; overflow:hidden; border:1px solid #e0e0e0;">
            <div style="background:#0f9d58; padding:20px; text-align:center;">
                <h1 style="color:#ffffff; margin:0; font-size:22px;">Mullerwallet</h1>
            </div>
            <div style="padding:30px; text-align:center;">
                <p style="font-size:15px; color:#333;">Hi ' . htmlspecialchars($toName) . ',</p>
                <p style="font-size:15px; color:#333;">Use the code below to continue:</p>
                <div style="font-size:32px; font-weight:bold; letter-spacing:8px; color:#0f9d58; margin:20px 0;">
                    ' . htmlspecialchars($otpCode) . '
                </div>
                <p style="font-size:13px; color:#777;">This code expires in ' . OTP_EXPIRY_MINUTES . ' minutes.</p>
                <p style="font-size:13px; color:#999; margin-top:30px;">If you didn\'t request this, you can safely ignore this email.</p>
            </div>
        </div>
    </div>';

    return sendMail($toEmail, $toName, $subject, $htmlBody);
}

// ---------------------------------------------------------
// VALIDATION HELPERS
// ---------------------------------------------------------

function isValidEmail(string $email): bool
{
    return (bool) filter_var($email, FILTER_VALIDATE_EMAIL);
}

/**
 * Basic password strength check: min 8 chars, at least 1 letter + 1 number.
 */
function isStrongPassword(string $password): bool
{
    return strlen($password) >= 8
        && preg_match('/[A-Za-z]/', $password)
        && preg_match('/[0-9]/', $password);
}

function sanitizeInput(string $value): string
{
    return htmlspecialchars(trim($value), ENT_QUOTES, 'UTF-8');
}

// ---------------------------------------------------------
// USER HELPERS
// ---------------------------------------------------------

function findUserByEmail(PDO $pdo, string $email): array|false
{
    $stmt = $pdo->prepare("SELECT * FROM users WHERE email = :email LIMIT 1");
    $stmt->execute([':email' => $email]);
    return $stmt->fetch();
}

function findUserByPhone(PDO $pdo, string $phone): array|false
{
    $stmt = $pdo->prepare("SELECT * FROM users WHERE phone_number = :phone LIMIT 1");
    $stmt->execute([':phone' => $phone]);
    return $stmt->fetch();
}

// ---------------------------------------------------------
// TRANSACTION PIN HELPERS
// ---------------------------------------------------------

function isValidPinFormat(string $pin): bool
{
    return (bool) preg_match('/^[0-9]{4}$/', $pin);
}

/**
 * Set (or change) a user's transaction PIN. Caller must have
 * already verified the old PIN (if one exists) before calling this.
 */
function setUserPin(PDO $pdo, int $userId, string $newPin): void
{
    $stmt = $pdo->prepare(
        "UPDATE users SET pin_hash = :hash, pin_set = 1, pin_attempts = 0, pin_locked_until = NULL
         WHERE id = :id"
    );
    $stmt->execute([':hash' => password_hash($newPin, PASSWORD_DEFAULT), ':id' => $userId]);
}

/**
 * Verify a submitted transaction PIN, with lockout after too many failed tries.
 *
 * @return array ['success' => bool, 'message' => string]
 */
function verifyUserPin(PDO $pdo, array $user, string $submittedPin): array
{
    if ((int) $user['pin_set'] !== 1 || empty($user['pin_hash'])) {
        return ['success' => false, 'message' => 'You have not set a transaction PIN yet.'];
    }

    if (!empty($user['pin_locked_until']) && strtotime($user['pin_locked_until']) > time()) {
        $minsLeft = ceil((strtotime($user['pin_locked_until']) - time()) / 60);
        return ['success' => false, 'message' => 'PIN locked. Try again in ' . $minsLeft . ' minute(s).'];
    }

    if (!password_verify($submittedPin, $user['pin_hash'])) {
        $attempts = (int) $user['pin_attempts'] + 1;

        if ($attempts >= PIN_MAX_ATTEMPTS) {
            $lockUntil = date('Y-m-d H:i:s', strtotime('+' . PIN_LOCKOUT_MINUTES . ' minutes'));
            $stmt = $pdo->prepare("UPDATE users SET pin_attempts = :a, pin_locked_until = :l WHERE id = :id");
            $stmt->execute([':a' => $attempts, ':l' => $lockUntil, ':id' => $user['id']]);
            return ['success' => false, 'message' => 'Too many failed attempts. PIN locked for ' . PIN_LOCKOUT_MINUTES . ' minutes.'];
        }

        $stmt = $pdo->prepare("UPDATE users SET pin_attempts = :a WHERE id = :id");
        $stmt->execute([':a' => $attempts, ':id' => $user['id']]);
        return ['success' => false, 'message' => 'Incorrect PIN.'];
    }

    // Correct PIN — reset attempt counter
    $pdo->prepare("UPDATE users SET pin_attempts = 0, pin_locked_until = NULL WHERE id = :id")
        ->execute([':id' => $user['id']]);

    return ['success' => true, 'message' => 'PIN verified.'];
}

// ---------------------------------------------------------
// KYC / BVN HELPERS
// ---------------------------------------------------------

/**
 * Loosely compare two names (case-insensitive, ignores extra spaces),
 * since BVN records and user-entered names don't always match exactly
 * in formatting/spacing.
 */
function namesReasonablyMatch(string $a, string $b): bool
{
    $normalize = fn($s) => strtolower(trim(preg_replace('/\s+/', ' ', $s)));
    return $normalize($a) === $normalize($b);
}

/**
 * Mark a user's BVN as verified and upgrade their wallet to Tier 2.
 */
function markBvnVerified(PDO $pdo, int $userId, string $bvn): void
{
    $last4 = substr($bvn, -4);

    $stmt = $pdo->prepare(
        "UPDATE users SET bvn_verified = 1, bvn_last4 = :last4, bvn_verified_at = NOW() WHERE id = :id"
    );
    $stmt->execute([':last4' => $last4, ':id' => $userId]);

    $pdo->prepare("UPDATE wallets SET tier = 2 WHERE user_id = :id AND tier < 2")
        ->execute([':id' => $userId]);
}

function getTierLimits(int $tier): array
{
    return $tier >= 2
        ? ['daily' => TIER_2_DAILY_LIMIT, 'max_balance' => TIER_2_MAX_BALANCE]
        : ['daily' => TIER_1_DAILY_LIMIT, 'max_balance' => TIER_1_MAX_BALANCE];
}

/**
 * Create a wallet with a unique 10-digit account number for a new user.
 * Safe to call once per user (account_number and user_id are both UNIQUE).
 */
function createWalletForUser(PDO $pdo, int $userId): string
{
    do {
        $accountNumber = (string) random_int(1000000000, 9999999999);
        $stmt = $pdo->prepare("SELECT id FROM wallets WHERE account_number = :acc");
        $stmt->execute([':acc' => $accountNumber]);
    } while ($stmt->fetch());

    $stmt = $pdo->prepare(
        "INSERT INTO wallets (user_id, account_number, balance, tier)
         VALUES (:user_id, :acc, 0.00, 1)"
    );
    $stmt->execute([':user_id' => $userId, ':acc' => $accountNumber]);

    return $accountNumber;
}

function getWalletByUserId(PDO $pdo, int $userId): array|false
{
    $stmt = $pdo->prepare("SELECT * FROM wallets WHERE user_id = :user_id LIMIT 1");
    $stmt->execute([':user_id' => $userId]);
    return $stmt->fetch();
}

/**
 * Fetch the most recent transactions for a user (for dashboard activity feed).
 */
function getRecentTransactions(PDO $pdo, int $userId, int $limit = 5): array
{
    $stmt = $pdo->prepare(
        "SELECT * FROM transactions WHERE user_id = :user_id
         ORDER BY created_at DESC LIMIT :limit"
    );
    $stmt->bindValue(':user_id', $userId, PDO::PARAM_INT);
    $stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
    $stmt->execute();
    return $stmt->fetchAll();
}

/**
 * Sum of debits in the last 7 days (for "this week's spend" card).
 */
function getWeeklySpend(PDO $pdo, int $userId): float
{
    $stmt = $pdo->prepare(
        "SELECT COALESCE(SUM(amount), 0) FROM transactions
         WHERE user_id = :user_id AND type = 'debit' AND status = 'successful'
         AND created_at >= (NOW() - INTERVAL 7 DAY)"
    );
    $stmt->execute([':user_id' => $userId]);
    return (float) $stmt->fetchColumn();
}

function findUserById(PDO $pdo, int $id): array|false
{
    $stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id LIMIT 1");
    $stmt->execute([':id' => $id]);
    return $stmt->fetch();
}
