<?php

namespace App\Controllers;

use App\Models\PasswordResetCodeModel;
use App\Models\UserModel;
use Throwable;

/**
 * Self-service password reset via SMS (Hubtel), not email — this app has
 * no email-sending infrastructure, and SMS reuses the Hubtel integration
 * already built in Phase 3. The code is sent to the phone already on file
 * for the account, never to a phone the requester types in, so this can't
 * be used to hijack an account by supplying someone else's number.
 *
 * A staff account with no phone on file can't self-serve and is told to
 * contact their manager (who can reset it directly from Staff Users).
 */
class ForgotPasswordController extends BaseController
{
    public function showRequestForm()
    {
        return view('auth/forgot_password', ['pageTitle' => 'Forgot Password']);
    }

    public function requestCode()
    {
        $email = trim((string) $this->request->getPost('email'));
        $users = new UserModel();
        $user  = $users->where('email', $email)->where('is_active', 1)->first();

        // Same generic message whether or not the email matches an account,
        // so this can't be used to enumerate staff email addresses.
        $genericMessage = 'If that email has an account with a phone number on file, we\'ve sent a reset code by SMS.';

        if ($user === null) {
            return redirect()->to('login')->with('success', $genericMessage);
        }

        if (empty($user->phone)) {
            // Telling THIS user their account has no phone on file does
            // leak that the account exists, but it's necessary information
            // — without it they have no way to know why nothing arrived.
            return redirect()->back()->withInput()->with('error', 'This account has no phone number on file. Ask your manager to reset your password from Staff Users.');
        }

        $code = (new PasswordResetCodeModel())->generateFor($user->id);

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

            if (! $result['success']) {
                throw new \RuntimeException('SMS send reported failure');
            }
        } catch (Throwable $e) {
            log_message('error', 'Password reset SMS failed: ' . $e->getMessage());

            return redirect()->back()->withInput()->with('error', 'Could not send the reset code right now — please try again shortly, or ask your manager to reset your password from Staff Users.');
        }

        session()->set('password_reset_user_id', $user->id);
        session()->set('password_reset_phone_masked', $this->maskPhone($user->phone));

        $this->logActivity('auth', "Requested a password reset code for {$user->email}.");

        return redirect()->to('forgot-password/verify')->with('success', $genericMessage);
    }

    public function showVerifyForm()
    {
        if (! session()->has('password_reset_user_id')) {
            return redirect()->to('forgot-password')->with('error', 'Request a reset code first.');
        }

        return view('auth/reset_password', [
            'pageTitle'    => 'Reset Password',
            'maskedPhone'  => session('password_reset_phone_masked'),
        ]);
    }

    public function resendCode()
    {
        $userId = session('password_reset_user_id');

        if (! $userId) {
            return redirect()->to('forgot-password')->with('error', 'Request a reset code first.');
        }

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

        if ($user === null || empty($user->phone)) {
            return redirect()->to('forgot-password')->with('error', 'Request a reset code first.');
        }

        $code = (new PasswordResetCodeModel())->generateFor($user->id);

        try {
            service('hubtelService')->sendSms(
                $user->phone,
                "Your MaxYields PMS password reset code is {$code}. It expires in 10 minutes.",
            );
        } catch (Throwable $e) {
            log_message('error', 'Password reset SMS resend failed: ' . $e->getMessage());
        }

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

    public function resetPassword()
    {
        $userId = session('password_reset_user_id');

        if (! $userId) {
            return redirect()->to('forgot-password')->with('error', 'Request a reset code first.');
        }

        $code        = trim((string) $this->request->getPost('code'));
        $newPassword = (string) $this->request->getPost('password');
        $confirm     = (string) $this->request->getPost('password_confirm');

        $codes = new PasswordResetCodeModel();

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

        if (strlen($newPassword) < 8) {
            return redirect()->back()->with('error', 'Password must be at least 8 characters.');
        }

        if ($newPassword !== $confirm) {
            return redirect()->back()->with('error', 'Passwords do not match.');
        }

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

        if ($user === null) {
            return redirect()->to('forgot-password')->with('error', 'Request a reset code first.');
        }

        $users->skipValidation(true)->update($userId, ['password_hash' => password_hash($newPassword, PASSWORD_DEFAULT)]);
        $codes->consume((int) $userId, $code);

        session()->remove(['password_reset_user_id', 'password_reset_phone_masked']);

        $this->logActivity('auth', "{$user->full_name} reset their password via SMS code.");

        return redirect()->to('login')->with('success', 'Password reset — you can now log in.');
    }

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