import { Router } from "express";
import { UserHandler } from "../../handlers/UserHandler.js";
import { CreateUserRequest } from "../dtos/CreateUserRequest.js";
import { UserRepository } from "../../repositories/UserRepository.js";
import { UserDtoEntityMapper } from "../../mappers/UserDtoEntityMapper.js";
import { asyncHandler } from "../../utils/asyncHandler.js";
import { CreateUserResponse } from "../dtos/CreateUserResponse.js";
import { UserDto } from "../dtos/UserDto.js";
import {
    requireAdmin,
    requireAdminOrOwner,
    requireAdminOrSelf
} from "../../middleware/authorizationMiddleware.js";
import { UserListResponse } from "../dtos/UserListResponse.js";
import { HttpStatusCode } from "../apiHelpers/HttpStatusCodes.js";
import { InternalServerError, NotFoundError } from "../errors/httpErrors.js";
import { ChangeUserPasswordRequest } from "../dtos/ChangeUserPasswordRequest.js";

export const userBasicRoute = "/user";

/**
 * Handles all user related routes
 */
const router = Router();

// Inject repo + mapper here
const handler = new UserHandler(new UserRepository(), new UserDtoEntityMapper());

/**
 * Create a new user (admin only)
 * Consumes CreateUserRequest
 * Responds with UserDto
 */
router.post(
    "/create",
    requireAdmin,
    asyncHandler(async (req, res) => {
        const request: CreateUserRequest = req.body;
        const user: UserDto = await handler.createUser(request);
        const response: CreateUserResponse = { userData: user };
        res.status(HttpStatusCode.CREATED).json(response);
    })
);

/**
 * Update existing user
 * Consumes UserDto
 * Responds with UserDto
 * Does not allow for password change. Use change-password instead
 *
 * Users can update their own data, admins can update any user
 */
router.post(
    "/update",
    requireAdminOrSelf, // Checks req.body.id or req.body.userId
    asyncHandler(async (req, res) => {
        const dto: UserDto = req.body;
        const response = await handler.updateUserData(dto);
        res.status(HttpStatusCode.OK).json(response);
    })
);

/**
 * Update password of existing user
 * Consumes ChangeUserPasswordRequest
 * Responds with code 200 indicating success
 *
 * Users can change their own password, admins can change any password
 */
router.post(
    "/change-password",
    requireAdminOrOwner(req => req.body.userId),
    asyncHandler(async (req, res) => {
        const requestData: ChangeUserPasswordRequest = req.body;
        const success = await handler.changePassword(requestData);
        if (!success) {
            throw new InternalServerError("Failed to change password");
        }
        res.status(HttpStatusCode.OK).json({ message: "Password changed successfully" });
    })
);

/**
 * Get all users (admin only)
 * Responds with array of UserDto
 */
router.get(
    "/all",
    requireAdmin,
    asyncHandler(async (req, res) => {
        const users = await handler.getAllUsers();
        const response: UserListResponse = { valueList: users };
        res.status(HttpStatusCode.OK).json(response);
    })
);

/**
 * Get user data for current user
 * Responds with UserDto
 */
router.get(
    "/me",
    asyncHandler(async (req, res) => {
        const id = req.currentUser?.id;
        if (id) {
            const responseDto = await handler.getUserById(id);
            res.status(HttpStatusCode.OK).json(responseDto);
        } else {
            throw new NotFoundError("There is no user id for current session!");
        }
    })
);

/**
 * Get specific user by ID
 * Responds with UserDto
 *
 * Users can get their own data, admins can get any user's data
 */
router.get(
    "/:userId",
    requireAdminOrOwner(req => req.params.userId),
    asyncHandler(async (req, res) => {
        const userId = req.params.userId;
        const responseDto = await handler.getUserById(userId);
        res.status(HttpStatusCode.OK).json(responseDto);
    })
);

export default router;