<?php

namespace Modules\Auth\Services;

use Exception;
use Carbon\Carbon;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Password;
use Modules\Auth\Repositories\AuthRepository;
use Modules\Auth\Transformers\AuthUserResource;
use Modules\Core\Enums\StatusEnum;
use Modules\Core\Helpers\ApiResponse;
use PHPOpenSourceSaver\JWTAuth\Facades\JWTAuth;
use PHPOpenSourceSaver\JWTAuth\Exceptions\JWTException;
use PHPOpenSourceSaver\JWTAuth\Exceptions\TokenBlacklistedException;
use PHPOpenSourceSaver\JWTAuth\Exceptions\TokenExpiredException;
use PHPOpenSourceSaver\JWTAuth\Exceptions\TokenInvalidException;

class AuthService
{
    protected $authRepo;

    public function __construct(AuthRepository $authRepo)
    {
        $this->authRepo = $authRepo;
    }

    public function login(array $credentials)
    {
        try {
            $userCheck = $this->authRepo->findByEmail($credentials['email']);

            if (!$userCheck) {
                return ApiResponse::errorResponse('No account found with this email address.', 404);
            }

            if (!$token = JWTAuth::attempt($credentials)) {
                return ApiResponse::errorResponse('Incorrect email or password.', 401);
            }

            $user = JWTAuth::user();

            if (!$user || !$user->hasVerifiedEmail()) {
                JWTAuth::invalidate($token);
                return ApiResponse::errorResponse('Please verify your email before logging in.', 403);
            }

            if ($user->status === StatusEnum::INACTIVE) {
                JWTAuth::invalidate($token);
                return ApiResponse::errorResponse('Your account is inactive. Please contact support.', 403);
            }

            $user = $this->authRepo->loadLoginRelations($user);

            return (new AuthUserResource($user))->additional([
                'authorization' => $this->respondWithToken($token),
                'status' => true,
                'message' => 'Logged in successfully.'
            ]);
        } catch (JWTException $e) {
            return ApiResponse::errorResponse('Token creation failed. Please try again.', 500);
        } catch (Exception $e) {
            return ApiResponse::errorResponse('Unexpected error occurred during login.', 500);
        }
    }

    protected function respondWithToken($token)
    {
        $payload = JWTAuth::setToken($token)->getPayload();
        $issuedAt = $payload->get('iat');
        $refreshTTL = config('jwt.refresh_ttl');
        $refreshExpiresAt = Carbon::createFromTimestamp($issuedAt)->addMinutes($refreshTTL);

        return [
            'token' => $token,
            'type' => 'bearer',
            'expires' => Auth::factory()->getTTL() * 60,
            'refresh_expires_at' => $refreshExpiresAt->toDateTimeString(),
        ];
    }

    public function logout()
    {
        try {
            JWTAuth::parseToken()->authenticate();
            JWTAuth::invalidate(JWTAuth::getToken());
            // Auth::logout();
            return ApiResponse::successResponse('Successfully logged out.');
        } catch (TokenBlacklistedException $e) {
            return ApiResponse::errorResponse('Token has already been blacklisted.', 401);
        } catch (TokenExpiredException $e) {
            return ApiResponse::errorResponse('Token has expired.', 401);
        } catch (TokenInvalidException $e) {
            return ApiResponse::errorResponse('Token is invalid.', 401);
        } catch (JWTException $e) {
            return ApiResponse::errorResponse('Token missing or malformed.', 401);
        }   catch (Exception $e) {
            return ApiResponse::errorResponse('Unexpected error during logout.', 500);
        }
    }

    public function refreshToken()
    {
        try {
            $success = $this->respondWithToken(Auth::refresh());

            return ApiResponse::successResponse('Refresh token return successfully.', data: $success);
        } catch (TokenBlacklistedException $e) {
            return ApiResponse::errorResponse('Token has already been refreshed.', 401);
        } catch (JWTException $e) {
            return ApiResponse::errorResponse('Token is invalid or expired.', 401);
        }
    }

    public function updatePassword(array $data)
    {
        $user = jwtAuthUser();

        if (!Hash::check($data['current_password'], $user->password)) {
            return ApiResponse::errorResponse(message: 'Current password is incorrect.', statusCode: 400);
        }

        $user->password = Hash::make($data['new_password']);
        $user->password_changed_at = now();
        $user->save();

        try {
            JWTAuth::invalidate(JWTAuth::getToken());
        } catch (JWTException $e) {
            // Silent fail if token already expired/invalid
        }

        $newToken = JWTAuth::fromUser($user);
        $user = $this->authRepo->loadLoginRelations($user);

        return (new AuthUserResource($user))->additional([
            'authorization' => $this->respondWithToken($newToken),
            'status' => true,
            'message' => 'Password changed successfully.'
        ]);
    }

    public function sendResetLink(array $data)
    {
        $status = Password::sendResetLink([
            'email' => $data['email']
        ]);

        if ($status === Password::RESET_LINK_SENT) {
            return ApiResponse::successResponse('Password reset link sent successfully.', 201);
        }

        return ApiResponse::errorResponse(
            __($status),
            400
        );
    }

    public function resetPassword(array $data)
    {
        $resetUser = null;

        $status = Password::reset(
            [
                'email' => $data['email'],
                'password' => $data['password'],
                'password_confirmation' => $data['password_confirmation'],
                'token' => $data['token'],
            ],
            function ($user, $password) use (&$resetUser) {
                $user->password = Hash::make($password);
                $user->password_changed_at = now();
                $user->save();

                $resetUser = [
                    'name'       => $user->name,
                    'email'      => $user->email,
                    'changed_at' => $user->password_changed_at?->toDateTimeString(),
                ];

                try {
                    if (JWTAuth::getToken()) {
                        JWTAuth::invalidate(JWTAuth::getToken());
                    }
                } catch (\Exception $e) {
                    // Silently fail if token can't be invalidated.
                }
            }
        );

        if ($status === Password::PASSWORD_RESET) {
            return ApiResponse::successResponse(
                'Password has been successfully reset. Please log in again.',
                201,
                $resetUser
            );
        }

        return ApiResponse::errorResponse('Invalid or expired reset token.', 400);
    }
}
