<?php
// ============================================================
//  MJPOS – AuthController
// ============================================================

class AuthController extends Controller
{
    public function loginPage(): void
    {
        if (Auth::check()) {
            $this->redirect(APP_URL . '/dashboard');
        }
        $pageTitle = 'Login';
        $this->loadView('auth.login', compact('pageTitle'));
    }

    public function login(): void
    {
        Auth::requireCsrf();

        $ip = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';

        // Check lockout BEFORE processing credentials
        if (Auth::isLockedOut($ip)) {
            $wait = Auth::lockoutRemaining($ip);
            $this->flash('error',
                'Too many failed attempts. Try again in '
                . ceil($wait / 60) . ' minute(s).');
            $this->redirect(APP_URL . '/auth/login');
            return;
        }

        $username = Security::strip($this->post('username', ''));
        $password = $this->post('password', '');

        if (empty($username) || empty($password)) {
            $this->flash('error', 'Username and password are required.');
            $this->redirect(APP_URL . '/auth/login');
            return;
        }

        if (Auth::attempt($username, $password)) {
            // Redirect to intended page or role default
            $intended  = $_SESSION['intended'] ?? '';
            unset($_SESSION['intended']);
            $fallback  = Auth::role() === 'cashier' ? APP_URL . '/pos' : APP_URL . '/dashboard';
            $this->redirect($intended ?: $fallback);
        } else {
            $remaining = MAX_LOGIN_ATTEMPTS - Auth::loginAttempts($ip);
            $msg = 'Invalid username or password.';
            if ($remaining <= 2 && $remaining > 0) {
                $msg .= " ({$remaining} attempt(s) left before lockout)";
            } elseif (Auth::isLockedOut($ip)) {
                $wait = Auth::lockoutRemaining($ip);
                $msg  = 'Account locked. Try again in ' . ceil($wait / 60) . ' minute(s).';
            }
            $this->flash('error', $msg);
            $this->redirect(APP_URL . '/auth/login');
        }
    }

    public function logout(): void
    {
        Auth::logout();
        $this->redirect(APP_URL . '/auth/login');
    }

    // ── Forgot Password ───────────────────────────────────────
    public function forgotPasswordForm(): void
    {
        if (Auth::check()) $this->redirect(APP_URL . '/dashboard');
        require APP_PATH . '/views/auth/forgot_password.php';
    }

    public function forgotPassword(): void
    {
        $username = trim($this->post('username', ''));
        if (!$username) {
            $this->flash('error', 'Please enter your username.');
            $this->redirect(APP_URL . '/auth/forgot-password');
            return;
        }

        $user = Database::fetchOne(
            "SELECT u.id, u.name, u.username, u.email, u.phone,
                    LOWER(TRIM(r.role_name)) AS role
             FROM users u
             INNER JOIN roles r ON r.id = u.role_id
             WHERE (u.username = ? OR u.email = ?)
               AND u.status = 'active'
             LIMIT 1",
            [$username, $username]
        );

        if (!$user) {
            $this->flash('error', 'Username not found. Please check and try again.');
            $this->redirect(APP_URL . '/auth/forgot-password');
            return;
        }

        // Developer role cannot reset via this flow
        if ($user['role'] === 'developer') {
            $this->flash('error', 'Developer account cannot be reset via this method. Contact the system developer directly.');
            $this->redirect(APP_URL . '/auth/forgot-password');
            return;
        }

        // Get admin reset phone from settings
        $adminPhone = setting('admin_reset_phone', '');

        // Fallback: find admin/super_admin user phone
        if (!$adminPhone) {
            $admin = Database::fetchOne(
                "SELECT u.phone
                 FROM users u
                 INNER JOIN roles r ON r.id = u.role_id
                 WHERE LOWER(REPLACE(TRIM(r.role_name), ' ', '_')) IN ('super_admin','admin')
                   AND u.status = 'active'
                   AND u.phone IS NOT NULL
                   AND TRIM(u.phone) <> ''
                 ORDER BY CASE WHEN LOWER(TRIM(r.role_name)) = 'super admin' THEN 0 ELSE 1 END,
                          u.id ASC
                 LIMIT 1"
            );
            $adminPhone = $admin['phone'] ?? '';
        }

        if (!$adminPhone) {
            $this->flash('error', 'Admin phone not configured. Please set "Admin Reset Phone" in Settings.');
            $this->redirect(APP_URL . '/auth/forgot-password');
            return;
        }

        // Check SMS is enabled
        if (setting('sms_enabled','0') !== '1') {
            $this->flash('error', 'SMS is not enabled. Please enable SMS in Settings first.');
            $this->redirect(APP_URL . '/auth/forgot-password');
            return;
        }

        // Invalidate old resets
        Database::execute(
            "UPDATE password_resets SET used = 1 WHERE user_id = ? AND used = 0",
            [$user['id']]
        );

        // Generate OTP (6 digits) and token
        $otp       = str_pad(random_int(100000, 999999), 6, '0', STR_PAD_LEFT);
        $token     = bin2hex(random_bytes(24));
        $expiresAt = date('Y-m-d H:i:s', strtotime('+15 minutes'));

        try {
            Database::execute(
                "INSERT INTO password_resets (user_id, token, otp, otp_verified, expires_at) VALUES (?,?,?,0,?)",
                [$user['id'], $token, $otp, $expiresAt]
            );
        } catch (\Throwable $e) {
            // Columns not yet added — run mjpos_otp_reset.sql in phpMyAdmin first
            $this->flash('error', 'Database not updated. Please import mjpos_otp_reset.sql first. Error: ' . $e->getMessage());
            $this->redirect(APP_URL . '/auth/forgot-password');
            return;
        }

        // Send OTP to admin phone via SMSlenz (inline cURL)
        $smsOk = false;
        if (function_exists('curl_init')) {
            $p = preg_replace('/[\s\-]/', '', $adminPhone);
            if (preg_match('/^0[0-9]{9}$/', $p))     $p = '+94' . substr($p, 1);
            elseif (preg_match('/^94\d{9}$/', $p))   $p = '+' . $p;
            elseif (preg_match('/^7[0-9]{8}$/', $p)) $p = '+94' . $p;

            $msg = 'MJPOS Password Reset OTP: ' . $otp .
                   ' | User: ' . $user['username'] .
                   ' | Expires: 15 min | Do not share.';

            $ch = curl_init();
            curl_setopt_array($ch, [
                CURLOPT_URL        => rtrim(setting('sms_api_url','https://smslenz.lk/api'),'/') . '/send-sms',
                CURLOPT_POST       => true,
                CURLOPT_POSTFIELDS => http_build_query([
                    'user_id'   => setting('sms_user_id',''),
                    'api_key'   => setting('sms_api_key',''),
                    'sender_id' => setting('sms_sender_id','SMSlenzDEMO'),
                    'contact'   => $p,
                    'message'   => $msg,
                ]),
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_TIMEOUT        => 10,
                CURLOPT_SSL_VERIFYPEER => false,
            ]);
            $raw   = curl_exec($ch);
            curl_close($ch);
            $resp  = $raw ? json_decode($raw, true) : null;
            $smsOk = $resp['success'] ?? false;
        }

        if (!$smsOk) {
            // SMS failed — still allow but warn
            $_SESSION['otp_warn'] = 'SMS delivery failed. Please contact the admin directly for OTP.';
        }

        // Store token in session for OTP page
        $_SESSION['otp_reset'] = [
            'token'       => $token,
            'username'    => $user['username'],
            'name'        => $user['name'],
            'admin_phone' => substr($adminPhone, 0, 4) . '****' . substr($adminPhone, -2), // masked
        ];

        Auth::logAudit(null, 'password_reset_otp_sent', 'auth',
            "OTP sent to admin for user: {$user['username']}");

        $this->redirect(APP_URL . '/auth/verify-otp');
    }

    // ── OTP Verification ─────────────────────────────────────
    public function verifyOtpForm(): void
    {
        if (Auth::check()) $this->redirect(APP_URL . '/dashboard');
        if (empty($_SESSION['otp_reset'])) {
            $this->redirect(APP_URL . '/auth/forgot-password');
            return;
        }
        require APP_PATH . '/views/auth/verify_otp.php';
    }

    public function verifyOtp(): void
    {
        if (empty($_SESSION['otp_reset'])) {
            $this->redirect(APP_URL . '/auth/forgot-password');
            return;
        }

        $enteredOtp = trim($this->post('otp', ''));
        $token      = $_SESSION['otp_reset']['token'];

        try {
            $reset = Database::fetchOne(
                "SELECT * FROM password_resets
                 WHERE token = ? AND used = 0 AND otp_verified = 0 AND expires_at > NOW()",
                [$token]
            );
        } catch (\Throwable $e) {
            $this->flash('error', 'Database error. Please import mjpos_otp_reset.sql first.');
            $this->redirect(APP_URL . '/auth/forgot-password');
            return;
        }

        if (!$reset) {
            $this->flash('error', 'OTP expired. Please request a new one.');
            unset($_SESSION['otp_reset']);
            $this->redirect(APP_URL . '/auth/forgot-password');
            return;
        }

        if ($reset['otp'] !== $enteredOtp) {
            $this->flash('error', 'Incorrect OTP. Please try again.');
            $this->redirect(APP_URL . '/auth/verify-otp');
            return;
        }

        // Mark OTP as verified
        Database::execute(
            "UPDATE password_resets SET otp_verified = 1 WHERE token = ?",
            [$token]
        );

        $this->redirect(APP_URL . '/auth/reset-password/' . $token);
    }

    // ── Reset Password ────────────────────────────────────────
    public function resetPasswordForm(string $token): void
    {
        if (Auth::check()) $this->redirect(APP_URL . '/dashboard');

        try {
            $reset = Database::fetchOne(
                "SELECT pr.*, u.name, u.username,
                        LOWER(TRIM(COALESCE(r.role_name, ''))) AS role
                 FROM password_resets pr
                 INNER JOIN users u ON u.id = pr.user_id
                 LEFT JOIN roles r ON r.id = u.role_id
                 WHERE pr.token = ?
                   AND pr.used = 0
                   AND pr.otp_verified = 1
                   AND pr.expires_at > NOW()
                 LIMIT 1",
                [$token]
            );
        } catch (\Throwable $e) {
            error_log('Reset password form error: ' . $e->getMessage());
            $this->flash('error', 'Unable to open the reset-password page. Please request a new OTP.');
            $this->redirect(APP_URL . '/auth/forgot-password');
            return;
        }

        if (!$reset) {
            $this->flash('error', 'Invalid or expired reset link. Please start again.');
            $this->redirect(APP_URL . '/auth/forgot-password');
            return;
        }

        require APP_PATH . '/views/auth/reset_password.php';
    }

    public function resetPassword(string $token): void
    {
        Auth::requireCsrf();

        $reset = Database::fetchOne(
            "SELECT * FROM password_resets
             WHERE token = ? AND used = 0 AND otp_verified = 1 AND expires_at > NOW()",
            [$token]
        );

        if (!$reset) {
            $this->flash('error', 'Invalid or expired reset link.');
            $this->redirect(APP_URL . '/auth/forgot-password');
            return;
        }

        // Block developer
        $user = Database::fetchOne(
            "SELECT LOWER(TRIM(r.role_name)) AS role
             FROM users u
             INNER JOIN roles r ON r.id = u.role_id
             WHERE u.id = ?",
            [$reset['user_id']]
        );
        if (($user['role'] ?? '') === 'developer') {
            $this->flash('error', 'Developer account cannot be reset via this method.');
            $this->redirect(APP_URL . '/auth/forgot-password');
            return;
        }

        $password = $this->post('password', '');
        $confirm  = $this->post('confirm_password', '');

        if (strlen($password) < 6) {
            $this->flash('error', 'Password must be at least 6 characters.');
            $this->redirect(APP_URL . '/auth/reset-password/' . $token);
            return;
        }
        if ($password !== $confirm) {
            $this->flash('error', 'Passwords do not match.');
            $this->redirect(APP_URL . '/auth/reset-password/' . $token);
            return;
        }

        $hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
        Database::execute("UPDATE users SET password_hash = ? WHERE id = ?", [$hash, $reset['user_id']]);
        Database::execute("UPDATE password_resets SET used = 1 WHERE token = ?", [$token]);

        unset($_SESSION['otp_reset']);
        Auth::logAudit($reset['user_id'], 'password_reset', 'auth', 'Password reset via OTP verified');

        $this->flash('success', 'Password reset successfully. Please login with your new password.');
        $this->redirect(APP_URL . '/auth/login');
    }
}