<?php

namespace App\Controllers;

use App\Models\LoginOtpCodeModel;
use App\Models\UserModel;
use Throwable;

/**
 * Two-step login: password, then an SMS code sent to the phone already
 * on file (never a phone the requester types in — can't be used to
 * receive someone else's code) — every staff login, every role, not
 * just password reset. isLoggedIn/userId/etc. are only ever set on
 * session by completeLogin(), reached either after a verified code or
 * (see attemptLogin()) when this hotel hasn't configured SMS at all.
 *
 * That second path matters: if SMS were hard-required, a hotel that
 * hasn't configured Hubtel yet would have every single staff member
 * permanently locked out the moment this shipped — nobody could ever
 * reach Settings to configure it, since reaching Settings requires
 * being logged in. Same "an unconfigured optional integration degrades,
 * it never blocks core functionality" rule this app already applies to
 * Hubtel/WhatsApp/email everywhere else (see HubtelService,
 * WhatsAppService, EmailService) — login is core functionality, so a
 * hotel with no SMS configured logs in with password alone (logged
 * clearly, so a manager can see the gap and close it) rather than being
 * locked out. Once SMS *is* configured, every login requires the code —
 * this only ever skips the check when sending one is genuinely
 * impossible, not when a send merely fails (that still blocks and asks
 * the user to retry, since that's more likely transient).
 */
class AuthController extends BaseController
{
    public function showLogin()
    {
        if (session('isLoggedIn')) {
            return redirect()->to('/');
        }

        return view('auth/login', ['pageTitle' => 'Log In']);
    }

    public function attemptLogin()
    {
        $rules = [
            'email'    => 'required|valid_email',
            'password' => 'required',
        ];

        if (! $this->validate($rules)) {
            return redirect()->back()->withInput()->with('validation', $this->validator);
        }

        $email    = $this->request->getPost('email');
        $password = $this->request->getPost('password');

        $user = (new UserModel())->verifyCredentials($email, $password);

        if ($user === null) {
            return redirect()->back()->withInput()->with('error', 'Invalid email or password.');
        }

        if (! $this->smsConfigured()) {
            return $this->completeLogin($user, ' (no SMS configured for this hotel yet — logged in without a code; add Hubtel SMS under Settings to turn this on)');
        }

        if (empty($user->phone)) {
            return redirect()->back()->withInput()->with('error', 'Your account has no phone number on file, so a login code can\'t be sent. Ask your manager to add one under Staff.');
        }

        if (! $this->sendOtp($user)) {
            return redirect()->back()->withInput()->with('error', 'Could not send your login code right now — please try again shortly.');
        }

        session()->set([
            'pending_login_user_id'      => $user->id,
            'pending_login_phone_masked' => $this->maskPhone($user->phone),
        ]);

        $this->logActivity('auth', "{$user->full_name} passed the password check, awaiting login code.");

        return redirect()->to('login/verify');
    }

    public function showVerify()
    {
        if (! session()->has('pending_login_user_id')) {
            return redirect()->to('login')->with('error', 'Log in with your password first.');
        }

        return view('auth/login_verify', [
            'pageTitle'   => 'Verification Code',
            'maskedPhone' => session('pending_login_phone_masked'),
        ]);
    }

    public function verifyOtp()
    {
        $userId = session('pending_login_user_id');

        if (! $userId) {
            return redirect()->to('login')->with('error', 'Log in with your password first.');
        }

        $code  = trim((string) $this->request->getPost('code'));
        $codes = new LoginOtpCodeModel();

        if (! $codes->isValid((int) $userId, $code)) {
            return redirect()->back()->with('error', 'That code is invalid or has expired.');
        }

        $user = (new UserModel())->find($userId);

        if ($user === null) {
            session()->remove(['pending_login_user_id', 'pending_login_phone_masked']);

            return redirect()->to('login')->with('error', 'Log in with your password first.');
        }

        $codes->consume((int) $userId, $code);
        session()->remove(['pending_login_user_id', 'pending_login_phone_masked']);

        return $this->completeLogin($user);
    }

    public function resendOtp()
    {
        $userId = session('pending_login_user_id');

        if (! $userId) {
            return redirect()->to('login')->with('error', 'Log in with your password first.');
        }

        $user = (new UserModel())->find($userId);

        if ($user === null || empty($user->phone)) {
            return redirect()->to('login')->with('error', 'Log in with your password first.');
        }

        $this->sendOtp($user);

        return redirect()->to('login/verify')->with('success', 'A new code has been sent.');
    }

    public function logout()
    {
        if (session('isLoggedIn')) {
            $this->logActivity('auth', session('userName') . ' logged out.');
        }

        session()->destroy();

        return redirect()->to('/login')->with('success', 'You have been logged out.');
    }

    private function completeLogin(object $user, string $activityNote = '')
    {
        $session = session();
        $session->regenerate();
        $session->set([
            'isLoggedIn' => true,
            'userId'     => $user->id,
            'userName'   => $user->full_name,
            'userEmail'  => $user->email,
            'userRole'   => $user->role,
        ]);

        $this->logActivity('auth', "{$user->full_name} logged in.{$activityNote}");

        return redirect()->to('/');
    }

    private function smsConfigured(): bool
    {
        $settings = service('settings');

        return $settings->isConfigured('hubtel.sms.client_id')
            && $settings->isConfigured('hubtel.sms.client_secret')
            && $settings->isConfigured('hubtel.sms.sender_id');
    }

    private function sendOtp(object $user): bool
    {
        $code = (new LoginOtpCodeModel())->generateFor($user->id);

        try {
            $result = service('hubtelService')->sendSms(
                $user->phone,
                "Your MaxYields PMS login code is {$code}. It expires in 10 minutes. If this wasn't you, ignore this message.",
            );

            return $result['success'];
        } catch (Throwable $e) {
            log_message('error', 'Login OTP SMS failed: ' . $e->getMessage());

            return false;
        }
    }

    private function maskPhone(string $phone): string
    {
        return str_repeat('•', max(0, strlen($phone) - 2)) . substr($phone, -2);
    }
}
