/**
 * @fileoverview User management endpoints for Aurora API.
 * Provides CRUD operations for users and user inventory.
 */

import type { RouteContext, RouteModule } from "./types";
import {
    jsonResponse,
    errorResponse,
    parseBody,
    parseIdFromPath,
    parseStringIdFromPath,
    withErrorHandling
} from "./utils";
import { UpdateUserSchema, InventoryAddSchema } from "./schemas";

/**
 * Users routes handler.
 * 
 * Endpoints:
 * - GET /api/users - List users with filters
 * - GET /api/users/:id - Get single user
 * - PUT /api/users/:id - Update user
 * - GET /api/users/:id/inventory - Get user inventory
 * - POST /api/users/:id/inventory - Add item to inventory
 * - DELETE /api/users/:id/inventory/:itemId - Remove item from inventory
 */
async function handler(ctx: RouteContext): Promise<Response | null> {
    const { pathname, method, req, url } = ctx;

    // Only handle requests to /api/users*
    if (!pathname.startsWith("/api/users")) {
        return null;
    }

    /**
     * @route GET /api/users
     * @description Returns a paginated list of users with optional filtering and sorting.
     * 
     * @query search - Filter by username (partial match)
     * @query sortBy - Sort field: balance, level, xp, username (default: balance)
     * @query sortOrder - Sort direction: asc, desc (default: desc)
     * @query limit - Max results (default: 50)
     * @query offset - Pagination offset (default: 0)
     * 
     * @response 200 - `{ users: User[], total: number }`
     * @response 500 - Error fetching users
     * 
     * @example
     * // Request
     * GET /api/users?sortBy=level&sortOrder=desc&limit=10
     */
    if (pathname === "/api/users" && method === "GET") {
        return withErrorHandling(async () => {
            const { users } = await import("@shared/db/schema");
            const { DrizzleClient } = await import("@shared/db/DrizzleClient");
            const { ilike, desc, asc, sql } = await import("drizzle-orm");

            const search = url.searchParams.get("search") || undefined;
            const sortBy = url.searchParams.get("sortBy") || "balance";
            const sortOrder = url.searchParams.get("sortOrder") || "desc";
            const limit = url.searchParams.get("limit") ? parseInt(url.searchParams.get("limit")!) : 50;
            const offset = url.searchParams.get("offset") ? parseInt(url.searchParams.get("offset")!) : 0;

            let query = DrizzleClient.select().from(users);

            if (search) {
                query = query.where(ilike(users.username, `%${search}%`)) as typeof query;
            }

            const sortColumn = sortBy === "level" ? users.level :
                sortBy === "xp" ? users.xp :
                    sortBy === "username" ? users.username : users.balance;
            const orderFn = sortOrder === "asc" ? asc : desc;

            const result = await query
                .orderBy(orderFn(sortColumn))
                .limit(limit)
                .offset(offset);

            const countResult = await DrizzleClient.select({ count: sql<number>`count(*)` }).from(users);
            const total = Number(countResult[0]?.count || 0);

            return jsonResponse({ users: result, total });
        }, "fetch users");
    }

    /**
     * @route GET /api/users/:id
     * @description Returns a single user by Discord ID.
     * Includes related class information if the user has a class assigned.
     * 
     * @param id - Discord User ID (snowflake)
     * @response 200 - Full user object with class relation
     * @response 404 - User not found
     * @response 500 - Error fetching user
     * 
     * @example
     * // Response
     * {
     *   "id": "123456789012345678",
     *   "username": "Player1",
     *   "balance": "1000",
     *   "level": 5,
     *   "class": { "id": "1", "name": "Warrior" }
     * }
     */
    if (pathname.match(/^\/api\/users\/\d+$/) && method === "GET") {
        const id = parseStringIdFromPath(pathname);
        if (!id) return null;

        return withErrorHandling(async () => {
            const { userService } = await import("@shared/modules/user/user.service");
            const user = await userService.getUserById(id);

            if (!user) {
                return errorResponse("User not found", 404);
            }

            return jsonResponse(user);
        }, "fetch user");
    }

    /**
     * @route PUT /api/users/:id
     * @description Updates user fields. Only provided fields will be updated.
     * 
     * @param id - Discord User ID (snowflake)
     * @body {
     *   username?: string,
     *   balance?: string | number,
     *   xp?: string | number,
     *   level?: number,
     *   dailyStreak?: number,
     *   isActive?: boolean,
     *   settings?: object,
     *   classId?: string | number
     * }
     * @response 200 - `{ success: true, user: User }`
     * @response 404 - User not found
     * @response 500 - Error updating user
     */
    if (pathname.match(/^\/api\/users\/\d+$/) && method === "PUT") {
        const id = parseStringIdFromPath(pathname);
        if (!id) return null;

        return withErrorHandling(async () => {
            const { userService } = await import("@shared/modules/user/user.service");
            const data = await req.json() as Record<string, any>;

            const existing = await userService.getUserById(id);
            if (!existing) {
                return errorResponse("User not found", 404);
            }

            // Build update data (only allow safe fields)
            const updateData: any = {};
            if (data.username !== undefined) updateData.username = data.username;
            if (data.balance !== undefined) updateData.balance = BigInt(data.balance);
            if (data.xp !== undefined) updateData.xp = BigInt(data.xp);
            if (data.level !== undefined) updateData.level = parseInt(data.level);
            if (data.dailyStreak !== undefined) updateData.dailyStreak = parseInt(data.dailyStreak);
            if (data.isActive !== undefined) updateData.isActive = Boolean(data.isActive);
            if (data.settings !== undefined) updateData.settings = data.settings;
            if (data.classId !== undefined) updateData.classId = BigInt(data.classId);

            const updatedUser = await userService.updateUser(id, updateData);
            return jsonResponse({ success: true, user: updatedUser });
        }, "update user");
    }

    /**
     * @route GET /api/users/:id/inventory
     * @description Returns user's inventory with item details.
     * 
     * @param id - Discord User ID (snowflake)
     * @response 200 - `{ inventory: InventoryEntry[] }`
     * @response 500 - Error fetching inventory
     * 
     * @example
     * // Response
     * {
     *   "inventory": [
     *     {
     *       "userId": "123456789",
     *       "itemId": 1,
     *       "quantity": "5",
     *       "item": { "id": 1, "name": "Health Potion", ... }
     *     }
     *   ]
     * }
     */
    if (pathname.match(/^\/api\/users\/\d+\/inventory$/) && method === "GET") {
        const id = pathname.split("/")[3] || "0";

        return withErrorHandling(async () => {
            const { inventoryService } = await import("@shared/modules/inventory/inventory.service");
            const inventory = await inventoryService.getInventory(id);
            return jsonResponse({ inventory });
        }, "fetch inventory");
    }

    /**
     * @route POST /api/users/:id/inventory
     * @description Adds an item to user's inventory.
     * 
     * @param id - Discord User ID (snowflake)
     * @body { itemId: number, quantity: string | number }
     * @response 201 - `{ success: true, entry: InventoryEntry }`
     * @response 400 - Missing required fields
     * @response 500 - Error adding item
     */
    if (pathname.match(/^\/api\/users\/\d+\/inventory$/) && method === "POST") {
        const id = pathname.split("/")[3] || "0";

        return withErrorHandling(async () => {
            const { inventoryService } = await import("@shared/modules/inventory/inventory.service");
            const data = await req.json() as Record<string, any>;

            if (!data.itemId || !data.quantity) {
                return errorResponse("Missing required fields: itemId, quantity", 400);
            }

            const entry = await inventoryService.addItem(id, data.itemId, BigInt(data.quantity));
            return jsonResponse({ success: true, entry }, 201);
        }, "add item to inventory");
    }

    /**
     * @route DELETE /api/users/:id/inventory/:itemId
     * @description Removes an item from user's inventory.
     * 
     * @param id - Discord User ID (snowflake)
     * @param itemId - Item ID to remove
     * @query amount - Quantity to remove (default: 1)
     * @response 204 - Item removed (no content)
     * @response 500 - Error removing item
     */
    if (pathname.match(/^\/api\/users\/\d+\/inventory\/\d+$/) && method === "DELETE") {
        const parts = pathname.split("/");
        const userId = parts[3] || "";
        const itemId = parseInt(parts[5] || "0");

        if (!userId) return null;

        return withErrorHandling(async () => {
            const { inventoryService } = await import("@shared/modules/inventory/inventory.service");

            const amount = url.searchParams.get("amount");
            const quantity = amount ? BigInt(amount) : 1n;

            await inventoryService.removeItem(userId, itemId, quantity);
            return new Response(null, { status: 204 });
        }, "remove item from inventory");
    }

    return null;
}

export const usersRoutes: RouteModule = {
    name: "users",
    handler
};
