<?php

namespace App\Controllers\View;

use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\RESTful\ResourceController;
use App\Models\UserModel;
use App\Models\AuthorizedUserModel;
use CodeIgniter\I18n\Time;

class AuthorizedUsersController extends ResourceController
{
    private const TOKEN_TTL_HOURS = 24;

    protected $userModel;
    protected $authorizedUserModel;

    public function __construct(private \CodeIgniter\HTTP\IncomingRequest $request)
    {
        $this->userModel = new UserModel();
        $this->authorizedUserModel = new AuthorizedUserModel();
    }

    private function requireLogin()
    {
        if (!session()->get('is_logged_in')) {
            return $this->failUnauthorized('Authentication required.');
        }

        return null;
    }

    private function requireOwnership(array $authorizedUser)
    {
        $userId = (int) session()->get('user_id');
        if ($userId <= 0 || (int) ($authorizedUser['user_id'] ?? 0) !== $userId) {
            return $this->failForbidden('You do not have access to this resource.');
        }

        return null;
    }

    private function hashToken(string $token): string
    {
        return hash('sha256', $token);
    }
    /**
     * Return a list of authorized users for the logged-in main user.
     *
     * @return ResponseInterface
     */
    public function index()
    {
        if ($resp = $this->requireLogin()) {
            return $resp;
        }

        $userId = session()->get('user_id');
        $authorizedUsers = $this->authorizedUserModel->where('user_id', $userId)->findAll();

        return $this->respond($authorizedUsers);
    }

    /**
     * Show a specific authorized user by ID.
     *
     * @param int|string|null $id
     * @return ResponseInterface
     */
    public function show($id = null)
    {
        if ($resp = $this->requireLogin()) {
            return $resp;
        }

        $authorizedUser = $this->authorizedUserModel->find($id);

        if (!$authorizedUser) {
            return $this->failNotFound('Authorized user not found.');
        }

        if ($resp = $this->requireOwnership($authorizedUser)) {
            return $resp;
        }

        return $this->respond($authorizedUser);
    }

    /**
     * Create a new authorized user (add by email).
     *
     * @return ResponseInterface
     */
    public function create()
    {
        if ($resp = $this->requireLogin()) {
            return $resp;
        }

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

        // Validate email
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            return $this->failValidationErrors('Invalid email address.');
        }

        $user = $this->userModel->where('email', $email)->first();

        if (!$user) {
            return $this->respondCreated(['message' => 'Authorized user added. A confirmation email has been sent.']);
        }

        // Generate a token for confirmation
        helper('text');
        $token = bin2hex(random_bytes(48));
        $tokenHash = $this->hashToken($token);

        // Add entry to the authorized_users table
        $this->authorizedUserModel->insert([
            'user_id' => session()->get('user_id'),  // Main user ID
            'authorized_user_id' => $user['id'],
            'email' => $email,
            'token' => $tokenHash,
            'status' => 'Pending'
        ]);

        // Send confirmation email to the authorized user
        $this->sendAuthorizedUserConfirmationEmail($email, $token);

        return $this->respondCreated(['message' => 'Authorized user added. A confirmation email has been sent.']);
    }

    /**
     * Update an existing authorized user.
     *
     * @param int|string|null $id
     * @return ResponseInterface
     */
    public function update($id = null)
    {
        if ($resp = $this->requireLogin()) {
            return $resp;
        }

        // Fetch the authorized user
        $authorizedUser = $this->authorizedUserModel->find($id);

        if (!$authorizedUser) {
            return $this->failNotFound('Authorized user not found.');
        }

        if ($resp = $this->requireOwnership($authorizedUser)) {
            return $resp;
        }

        // Update the authorized user’s information (e.g., email)
        $email = strtolower($this->request->getPost('email'));
        if ($email && filter_var($email, FILTER_VALIDATE_EMAIL)) {
            $authorizedUser['email'] = $email;
        }

        $this->authorizedUserModel->save($authorizedUser);

        return $this->respondUpdated(['message' => 'Authorized user information updated.']);
    }

    /**
     * Delete an authorized user.
     *
     * @param int|string|null $id
     * @return ResponseInterface
     */
    public function delete($id = null)
    {
        if ($resp = $this->requireLogin()) {
            return $resp;
        }

        $authorizedUser = $this->authorizedUserModel->find($id);

        if (!$authorizedUser) {
            return $this->failNotFound('Authorized user not found.');
        }

        if ($resp = $this->requireOwnership($authorizedUser)) {
            return $resp;
        }

        // Delete the authorized user record
        $this->authorizedUserModel->delete($id);

        return $this->respondDeleted(['message' => 'Authorized user deleted successfully.']);
    }

    /**
     * Confirms the authorized user's token and allows them to set a password.
     *
     * @return ResponseInterface
     */
    public function confirm()
    {
        $token = $this->request->getGet('token');

        if (!$token) {
            return $this->fail('Invalid confirmation link.');
        }

        $tokenHash = $this->hashToken($token);
        $authorizedUser = $this->authorizedUserModel
            ->groupStart()
            ->where('token', $tokenHash)
            ->orWhere('token', $token)
            ->groupEnd()
            ->where('created_at >=', Time::now()->subHours(self::TOKEN_TTL_HOURS)->toDateTimeString())
            ->first();

        if (!$authorizedUser) {
            return $this->fail('Invalid or expired confirmation link.');
        }

        // Mark the authorized user as active and rotate token for password setup
        $nextToken = bin2hex(random_bytes(48));
        $nextTokenHash = $this->hashToken($nextToken);
        $this->authorizedUserModel->update($authorizedUser['id'], [
            'status' => 'Active',
            'token'  => $nextTokenHash,
        ]);

        return redirect()->to('/set_authorized_user_password/' . $authorizedUser['authorized_user_id'] . '?token=' . $nextToken);
    }

    /**
     * Displays the form for the authorized user to set their password.
     *
     * @param int $authorizedUserId
     * @return ResponseInterface
     */
    public function setPassword($authorizedUserId)
    {
        $token = (string) $this->request->getGet('token');
        if ($token === '') {
            return $this->fail('Invalid confirmation link.');
        }

        $tokenHash = $this->hashToken($token);
        $authorizedUser = $this->authorizedUserModel
            ->groupStart()
            ->where('token', $tokenHash)
            ->orWhere('token', $token)
            ->groupEnd()
            ->where('authorized_user_id', $authorizedUserId)
            ->where('status', 'Active')
            ->where('updated_at >=', Time::now()->subHours(self::TOKEN_TTL_HOURS)->toDateTimeString())
            ->first();

        if (!$authorizedUser) {
            return $this->fail('Invalid or expired confirmation link.');
        }

        $user = $this->userModel->find($authorizedUserId);

        if (!$user) {
            return $this->failNotFound('User not found.');
        }

        return view('user/set_authorized_user_password', [
            'userId' => $authorizedUserId,
            'token'  => $token,
        ]);
    }

    /**
     * Processes the password for the authorized user.
     *
     * @return ResponseInterface
     */
    public function savePassword($authorizedUserId = null)
    {
        $validation = \Config\Services::validation();
        $validation->setRules([
            'password' => [
                'label' => 'Password',
                'rules' => 'required|min_length[8]|regex_match[/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@\\-=\\+*#$%&!?])[A-Za-z\\d@\\-=\\+*#$%&!?]{8,}$/]',
            ],
            'password_confirm' => 'required|matches[password]',
            'user_id' => 'required|integer',
            'token' => 'required',
        ]);

        if (!$this->validate($validation->getRules())) {
            return $this->failValidationErrors($validation->getErrors());
        }

        $userId = (int) $this->request->getPost('user_id');
        $token  = (string) $this->request->getPost('token');
        $authorizedUserId = $authorizedUserId !== null ? (int) $authorizedUserId : $userId;

        if ($userId <= 0 || $authorizedUserId <= 0 || $userId !== $authorizedUserId) {
            return $this->fail('Invalid request.');
        }

        $tokenHash = $this->hashToken($token);
        $authorizedUser = $this->authorizedUserModel
            ->groupStart()
            ->where('token', $tokenHash)
            ->orWhere('token', $token)
            ->groupEnd()
            ->where('authorized_user_id', $authorizedUserId)
            ->where('status', 'Active')
            ->where('updated_at >=', Time::now()->subHours(self::TOKEN_TTL_HOURS)->toDateTimeString())
            ->first();

        if (!$authorizedUser) {
            return $this->fail('Invalid or expired confirmation link.');
        }

        $user = $this->userModel->find($authorizedUserId);
        if (!$user) {
            return $this->failNotFound('User not found.');
        }

        $password = (string) $this->request->getPost('password');
        $hashedPassword = pbkdf2_hash($password);

        $this->userModel->update($authorizedUserId, ['password' => $hashedPassword]);
        $this->authorizedUserModel->update($authorizedUser['id'], ['token' => null]);

        return $this->respond(['message' => 'Password has been successfully set.']);
    }
    /**
     * Sends a confirmation email to the authorized user.
     *
     * @param string $email
     * @param string $token
     */
    private function sendAuthorizedUserConfirmationEmail($email, $token)
    {
        // Generate the confirmation link
        $confirmLink = site_url('/confirm_authorized_user?token=' . $token);

        // Compose the email message
        $message = "
        <p>You have been added as an authorized user for another account. Click the link below to confirm your access:</p>
        <p><a href='{$confirmLink}'>Confirm Access</a></p>
        <p>If you did not request this, please ignore this email.</p>
        ";

        // Create an instance of the EmailController
        $emailController = new \App\Controllers\View\EmailController();
        $subject = 'Authorized User Confirmation';

        // Send email
        if ($emailController->sendEmail($email, $subject, $message)) {
            log_message('info', 'Authorized user confirmation email sent to ' . $email);
        } else {
            log_message('error', 'Failed to send authorized user confirmation email to ' . $email);
        }
    }
}
