<?php

namespace App\Controllers\Api;

use App\Libraries\SmsExecutor;
use App\Models\MemberInvitationModel;
use App\Models\MemberKinModel;
use App\Models\MemberModel;
use App\Models\UserModel;

class AuthController extends ApiController
{
    protected $userModel;
    protected $memberModel;
    protected $memberInvitationModel;
    protected $memberKinModel;

    public function initController(\CodeIgniter\HTTP\RequestInterface $request, \CodeIgniter\HTTP\ResponseInterface $response, \Psr\Log\LoggerInterface $logger)
    {
        parent::initController($request, $response, $logger);

        try {
            $this->userModel = new UserModel();
            $this->memberModel = new MemberModel();
            $this->memberInvitationModel = new MemberInvitationModel();
            $this->memberKinModel = new MemberKinModel();
        } catch (\Exception $e) {
            log_message('error', 'AuthController init error: ' . $e->getMessage());
        }
    }

    public function register()
    {
        if (!$this->request->is('post')) {
            return $this->respondBadRequest('Invalid request method');
        }

        $data = $this->request->getJSON();

        if (!isset($data->token) || !isset($data->password)) {
            return $this->respondValidationError([
                'token' => 'Invitation token required',
                'password' => 'Password required',
            ]);
        }

        $invitation = $this->memberInvitationModel->getValidInvitationByToken($data->token, 'register');
        if (!$invitation) {
            return $this->respondBadRequest('Invitation link is invalid or expired.');
        }

        $member = $this->memberModel->find($invitation['member_id']);
        if (!$member) {
            return $this->respondNotFound();
        }

        if (!empty($member['user_id'])) {
            return $this->respondBadRequest('This invitation has already been used.');
        }

        $existingUser = $this->userModel->findByEmail($invitation['email']);
        if ($existingUser) {
            return $this->respondBadRequest('An account with this email already exists.');
        }

        $userData = [
            'email' => $invitation['email'],
            'password' => password_hash($data->password, PASSWORD_BCRYPT),
            'full_name' => $member['full_name'],
            'role' => 'member',
            'status' => 'active',
        ];

        if ($this->userModel->insert($userData)) {
            $userId = $this->userModel->insertID();

            $this->memberModel->update($member['id'], [
                'user_id' => $userId,
                'email' => $invitation['email'],
                'phone' => $invitation['phone'],
            ]);

            $this->memberInvitationModel->update($invitation['id'], [
                'accepted_at' => date('Y-m-d H:i:s'),
            ]);

            return $this->respondCreated([
                'message' => 'Account created successfully',
                'userId' => $userId
            ]);
        }

        return $this->respondBadRequest('Registration failed: ' . implode(', ', $this->userModel->errors()));
    }

    public function invitation($token)
    {
        $invitation = $this->memberInvitationModel->getValidInvitationByToken($token, 'register');
        if (!$invitation) {
            return $this->respond(['error' => 'Invitation link is invalid or expired.'], 404);
        }

        $member = $this->memberModel->find($invitation['member_id']);
        if (!$member) {
            return $this->respondNotFound();
        }

        return $this->respond([
            'token' => $invitation['token'],
            'email' => $invitation['email'],
            'phone' => $invitation['phone'],
            'expires_at' => $invitation['expires_at'],
            'member' => [
                'id' => $member['id'],
                'full_name' => $member['full_name'],
                'roll_no' => $member['roll_no'],
            ],
        ]);
    }

    public function publicUpdateInvitation($token)
    {
        $invitation = $this->memberInvitationModel->getValidInvitationByToken($token, 'public_update');
        if (!$invitation) {
            return $this->respond(['error' => 'Public update link is invalid or expired.'], 404);
        }

        $member = $this->memberModel->find($invitation['member_id']);
        if (!$member) {
            return $this->respondNotFound();
        }

        $beneficiaries = $this->memberKinModel->where('member_id', $member['id'])->orderBy('id', 'ASC')->findAll();
        $installmentOneBeneficiary = $this->memberKinModel->getSelectedInstallmentBeneficiaryByMember($member['id'], 1);
        $installmentTwoBeneficiary = $this->memberKinModel->getSelectedInstallmentBeneficiaryByMember($member['id'], 2);

        return $this->respond([
            'token' => $invitation['token'],
            'expires_at' => $invitation['expires_at'],
            'member' => [
                'id' => $member['id'],
                'full_name' => $member['full_name'],
                'roll_no' => $member['roll_no'],
                'email' => $member['email'],
                'phone' => $member['phone'],
            ],
            'beneficiaries' => $beneficiaries,
            'selected_installment_one_beneficiary' => $installmentOneBeneficiary,
            'selected_installment_two_beneficiary' => $installmentTwoBeneficiary,
            'total_percentage' => $this->memberKinModel->getTotalBenefitsByMember($member['id']),
        ]);
    }

    public function login()
    {
        if (!$this->request->is('post')) {
            return $this->respondBadRequest('Invalid request method');
        }

        $data = $this->request->getJSON();

        if (!isset($data->email) || !isset($data->password)) {
            return $this->respondValidationError([
                'email' => 'Email required',
                'password' => 'Password required'
            ]);
        }

        $user = $this->userModel->authenticate($data->email, $data->password);

        if (!$user) {
            return $this->respond(['error' => 'Invalid email or password'], 401);
        }

        if ($user['status'] !== 'active') {
            return $this->respond(['error' => 'Account is inactive'], 403);
        }

        $token = $this->generateToken($user['id'], $user['email'], $user['role']);

        return $this->respond([
            'message' => 'Login successful',
            'token' => $token,
            'user' => [
                'id' => $user['id'],
                'email' => $user['email'],
                'full_name' => $user['full_name'],
                'role' => $user['role'],
            ]
        ]);
    }

    public function profile()
    {
        if (!$this->isAuthenticated()) {
            return $this->respond(['error' => 'Unauthorized'], 401);
        }

        $user = $this->userModel->find($this->currentUser['userId']);

        if (!$user) {
            return $this->respondNotFound();
        }

        return $this->respond([
            'id' => $user['id'],
            'email' => $user['email'],
            'full_name' => $user['full_name'],
            'role' => $user['role'],
            'status' => $user['status'],
        ]);
    }

    public function updateProfile()
    {
        if (!$this->isAuthenticated()) {
            return $this->respond(['error' => 'Unauthorized'], 401);
        }

        if (!$this->request->is('put')) {
            return $this->respondBadRequest('Invalid request method');
        }

        $data = $this->request->getJSON();
        $userId = $this->currentUser['userId'];

        $updateData = [];
        if (isset($data->full_name)) {
            $updateData['full_name'] = $data->full_name;
        }

        if ($this->userModel->update($userId, $updateData)) {
            return $this->respond(['message' => 'Profile updated successfully']);
        }

        return $this->respondBadRequest('Update failed');
    }

    public function changePassword()
    {
        if (!$this->isAuthenticated()) {
            return $this->respond(['error' => 'Unauthorized'], 401);
        }

        if (!$this->request->is('post')) {
            return $this->respondBadRequest('Invalid request method');
        }

        $data = $this->request->getJSON();
        $userId = $this->currentUser['userId'];

        if (!isset($data->old_password) || !isset($data->new_password)) {
            return $this->respondValidationError([
                'old_password' => 'Old password required',
                'new_password' => 'New password required'
            ]);
        }

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

        if (!password_verify($data->old_password, $user['password'])) {
            return $this->respond(['error' => 'Old password is incorrect'], 401);
        }

        $updateData = [
            'password' => password_hash($data->new_password, PASSWORD_BCRYPT)
        ];

        if ($this->userModel->update($userId, $updateData)) {
            return $this->respond(['message' => 'Password changed successfully']);
        }

        return $this->respondBadRequest('Password change failed');
    }

    public function requestSmsToken()
    {
        if (!$this->request->is('post')) {
            return $this->respondBadRequest('Invalid request method');
        }

        $data = $this->request->getJSON();

        if (!isset($data->roll_no) || !isset($data->phone)) {
            return $this->respondValidationError([
                'roll_no' => 'Roll number required',
                'phone'   => 'Phone number required',
            ]);
        }

        $rollNo = trim($data->roll_no);
        $phone  = trim($data->phone);

        $member = $this->memberModel->getMemberByRollNo($rollNo);

        if (!$member) {
            return $this->respond(['error' => 'Nambari ya usajili haijapatikana'], 404);
        }

        // Normalize both phones for comparison
        $normalizePhone = function (string $p): string {
            $p = preg_replace('/\D/', '', $p);
            if (str_starts_with($p, '0') && strlen($p) === 10) {
                return '255' . substr($p, 1);
            }
            if (str_starts_with($p, '+')) {
                return ltrim($p, '+');
            }
            return $p;
        };

        $storedPhone  = $normalizePhone((string) ($member['phone'] ?? ''));
        $inputPhone   = $normalizePhone($phone);

        if (empty($storedPhone) || $storedPhone !== $inputPhone) {
            return $this->respond(['error' => 'Nambari ya simu haifanani na rekodi'], 400);
        }

        // Rate-limit: block if a valid unused token was requested in the last 60 seconds
        $recent = $this->db->table('sms_login_tokens')
            ->where('roll_no', $rollNo)
            ->where('used_at IS NULL')
            ->where('expires_at >', date('Y-m-d H:i:s'))
            ->where('created_at >', date('Y-m-d H:i:s', time() - 60))
            ->get()->getRowArray();

        if ($recent) {
            return $this->respond(['error' => 'Subiri sekunde 60 kisha jaribu tena'], 429);
        }

        $otp       = str_pad((string) random_int(0, 999999), 6, '0', STR_PAD_LEFT);
        $expiresAt = date('Y-m-d H:i:s', time() + 600); // 10 minutes

        $this->db->table('sms_login_tokens')->insert([
            'member_id'  => $member['id'],
            'roll_no'    => $rollNo,
            'phone'      => $inputPhone,
            'token'      => $otp,
            'expires_at' => $expiresAt,
            'created_at' => date('Y-m-d H:i:s'),
        ]);

        try {
            $sms = new SmsExecutor();
            $message = "AWTF: Token yako ya kuingia ni $otp. Itaisha baada ya dakika 10. Usishiriki na mtu yeyote.";
            $sms->sendSms($inputPhone, $message);
        } catch (\Exception $e) {
            log_message('error', 'SMS token send error: ' . $e->getMessage());
        }

        return $this->respond([
            'message'  => 'Token imetumwa kwa simu yako. Itaisha baada ya dakika 10.',
            'roll_no'  => $rollNo,
        ]);
    }

    public function verifySmsToken()
    {
        if (!$this->request->is('post')) {
            return $this->respondBadRequest('Invalid request method');
        }

        $data = $this->request->getJSON();

        if (!isset($data->roll_no) || !isset($data->token)) {
            return $this->respondValidationError([
                'roll_no' => 'Roll number required',
                'token'   => 'Token required',
            ]);
        }

        $rollNo = trim($data->roll_no);
        $token  = trim($data->token);

        $record = $this->db->table('sms_login_tokens')
            ->where('roll_no', $rollNo)
            ->where('token', $token)
            ->where('used_at IS NULL')
            ->where('expires_at >', date('Y-m-d H:i:s'))
            ->orderBy('id', 'DESC')
            ->get()->getRowArray();

        if (!$record) {
            return $this->respond(['error' => 'Token si sahihi au imeisha muda wake'], 401);
        }

        $member = $this->memberModel->find($record['member_id']);

        if (!$member) {
            return $this->respondNotFound();
        }

        if (empty($member['user_id'])) {
            return $this->respond(['error' => 'Akaunti ya mwanachama haijasajiliwa. Wasiliana na msimamizi.'], 403);
        }

        $user = $this->userModel->find($member['user_id']);

        if (!$user || $user['status'] !== 'active') {
            return $this->respond(['error' => 'Akaunti imefungwa. Wasiliana na msimamizi.'], 403);
        }

        // Mark token as used
        $this->db->table('sms_login_tokens')
            ->where('id', $record['id'])
            ->update(['used_at' => date('Y-m-d H:i:s')]);

        $jwtToken = $this->generateToken($user['id'], $user['email'], $user['role']);

        return $this->respond([
            'message' => 'Umeingia mfumoni kwa mafanikio',
            'token'   => $jwtToken,
            'user'    => [
                'id'        => $user['id'],
                'email'     => $user['email'],
                'full_name' => $user['full_name'],
                'role'      => $user['role'],
            ],
        ]);
    }
}
