/**
 * @fileoverview Items management endpoints for Aurora API.
 * Provides CRUD operations for game items with image upload support.
 */

import { join, resolve, dirname } from "path";
import type { RouteContext, RouteModule } from "./types";
import type { CreateItemDTO, UpdateItemDTO } from "@shared/modules/items/items.service";
import {
    jsonResponse,
    errorResponse,
    parseBody,
    parseIdFromPath,
    parseQuery,
    withErrorHandling
} from "./utils";
import { CreateItemSchema, UpdateItemSchema, ItemQuerySchema } from "./schemas";

// Resolve assets directory path
const currentDir = dirname(new URL(import.meta.url).pathname);
const assetsDir = resolve(currentDir, "../../../bot/assets/graphics/items");

/**
 * Validates image file by checking magic bytes.
 * Supports PNG, JPEG, WebP, and GIF formats.
 */
function validateImageFormat(bytes: Uint8Array): boolean {
    const isPNG = bytes[0] === 0x89 && bytes[1] === 0x50 && bytes[2] === 0x4E && bytes[3] === 0x47;
    const isJPEG = bytes[0] === 0xFF && bytes[1] === 0xD8 && bytes[2] === 0xFF;
    const isWebP = bytes[8] === 0x57 && bytes[9] === 0x45 && bytes[10] === 0x42 && bytes[11] === 0x50;
    const isGIF = bytes[0] === 0x47 && bytes[1] === 0x49 && bytes[2] === 0x46;

    return isPNG || isJPEG || isWebP || isGIF;
}

/** Maximum image file size: 15MB */
const MAX_IMAGE_SIZE = 15 * 1024 * 1024;

/**
 * Items routes handler.
 * 
 * Endpoints:
 * - GET /api/items - List items with filters
 * - POST /api/items - Create item (JSON or multipart with image)
 * - GET /api/items/:id - Get single item
 * - PUT /api/items/:id - Update item
 * - DELETE /api/items/:id - Delete item and asset
 * - POST /api/items/:id/icon - Upload/replace item icon
 */
async function handler(ctx: RouteContext): Promise<Response | null> {
    const { pathname, method, req, url } = ctx;

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

    const { itemsService } = await import("@shared/modules/items/items.service");

    /**
     * @route GET /api/items
     * @description Returns a paginated list of items with optional filtering.
     * 
     * @query search - Filter by name/description (partial match)
     * @query type - Filter by item type (CONSUMABLE, EQUIPMENT, etc.)
     * @query rarity - Filter by rarity (C, R, SR, SSR)
     * @query limit - Max results per page (default: 100, max: 100)
     * @query offset - Pagination offset (default: 0)
     * 
     * @response 200 - `{ items: Item[], total: number }`
     * @response 500 - Error fetching items
     * 
     * @example
     * // Request
     * GET /api/items?type=CONSUMABLE&rarity=R&limit=10
     * 
     * // Response
     * {
     *   "items": [{ "id": 1, "name": "Health Potion", ... }],
     *   "total": 25
     * }
     */
    if (pathname === "/api/items" && method === "GET") {
        return withErrorHandling(async () => {
            const filters = {
                search: url.searchParams.get("search") || undefined,
                type: url.searchParams.get("type") || undefined,
                rarity: url.searchParams.get("rarity") || undefined,
                limit: url.searchParams.get("limit") ? parseInt(url.searchParams.get("limit")!) : 100,
                offset: url.searchParams.get("offset") ? parseInt(url.searchParams.get("offset")!) : 0,
            };

            const result = await itemsService.getAllItems(filters);
            return jsonResponse(result);
        }, "fetch items");
    }

    /**
     * @route POST /api/items
     * @description Creates a new item. Supports JSON or multipart/form-data with image.
     * 
     * @body (JSON) {
     *   name: string (required),
     *   type: string (required),
     *   description?: string,
     *   rarity?: "C" | "R" | "SR" | "SSR",
     *   price?: string | number,
     *   usageData?: object
     * }
     * 
     * @body (Multipart) {
     *   data: JSON string with item fields,
     *   image?: File (PNG, JPEG, WebP, GIF - max 15MB)
     * }
     * 
     * @response 201 - `{ success: true, item: Item }`
     * @response 400 - Missing required fields or invalid image
     * @response 409 - Item name already exists
     * @response 500 - Error creating item
     */
    if (pathname === "/api/items" && method === "POST") {
        return withErrorHandling(async () => {
            const contentType = req.headers.get("content-type") || "";

            let itemData: CreateItemDTO | null = null;
            let imageFile: File | null = null;

            if (contentType.includes("multipart/form-data")) {
                const formData = await req.formData();
                const jsonData = formData.get("data");
                imageFile = formData.get("image") as File | null;

                if (typeof jsonData === "string") {
                    itemData = JSON.parse(jsonData) as CreateItemDTO;
                } else {
                    return errorResponse("Missing item data", 400);
                }
            } else {
                itemData = await req.json() as CreateItemDTO;
            }

            if (!itemData) {
                return errorResponse("Missing item data", 400);
            }

            // Validate required fields
            if (!itemData.name || !itemData.type) {
                return errorResponse("Missing required fields: name and type are required", 400);
            }

            // Check for duplicate name
            if (await itemsService.isNameTaken(itemData.name)) {
                return errorResponse("An item with this name already exists", 409);
            }

            // Set placeholder URLs if image will be uploaded
            const placeholderUrl = "/assets/items/placeholder.png";
            const createData = {
                name: itemData.name,
                description: itemData.description || null,
                rarity: itemData.rarity || "C",
                type: itemData.type,
                price: itemData.price ? BigInt(itemData.price) : null,
                iconUrl: itemData.iconUrl || placeholderUrl,
                imageUrl: itemData.imageUrl || placeholderUrl,
                usageData: itemData.usageData || null,
            };

            // Create the item
            const item = await itemsService.createItem(createData);

            // If image was provided, save it and update the item
            if (imageFile && item) {
                const buffer = await imageFile.arrayBuffer();
                const bytes = new Uint8Array(buffer);

                if (!validateImageFormat(bytes)) {
                    await itemsService.deleteItem(item.id);
                    return errorResponse("Invalid image file. Only PNG, JPEG, WebP, and GIF are allowed.", 400);
                }

                if (buffer.byteLength > MAX_IMAGE_SIZE) {
                    await itemsService.deleteItem(item.id);
                    return errorResponse("Image file too large. Maximum size is 15MB.", 400);
                }

                const fileName = `${item.id}.png`;
                const filePath = join(assetsDir, fileName);
                await Bun.write(filePath, buffer);

                const assetUrl = `/assets/items/${fileName}?v=${Date.now()}`;
                await itemsService.updateItem(item.id, {
                    iconUrl: assetUrl,
                    imageUrl: assetUrl,
                });

                const updatedItem = await itemsService.getItemById(item.id);
                return jsonResponse({ success: true, item: updatedItem }, 201);
            }

            return jsonResponse({ success: true, item }, 201);
        }, "create item");
    }

    /**
     * @route GET /api/items/:id
     * @description Returns a single item by ID.
     * 
     * @param id - Item ID (numeric)
     * @response 200 - Full item object
     * @response 404 - Item not found
     * @response 500 - Error fetching item
     */
    if (pathname.match(/^\/api\/items\/\d+$/) && method === "GET") {
        const id = parseIdFromPath(pathname);
        if (!id) return null;

        return withErrorHandling(async () => {
            const item = await itemsService.getItemById(id);
            if (!item) {
                return errorResponse("Item not found", 404);
            }
            return jsonResponse(item);
        }, "fetch item");
    }

    /**
     * @route PUT /api/items/:id
     * @description Updates an existing item.
     * 
     * @param id - Item ID (numeric)
     * @body Partial item fields to update
     * @response 200 - `{ success: true, item: Item }`
     * @response 404 - Item not found
     * @response 409 - Name already taken by another item
     * @response 500 - Error updating item
     */
    if (pathname.match(/^\/api\/items\/\d+$/) && method === "PUT") {
        const id = parseIdFromPath(pathname);
        if (!id) return null;

        return withErrorHandling(async () => {
            const data = await req.json() as Partial<UpdateItemDTO>;

            const existing = await itemsService.getItemById(id);
            if (!existing) {
                return errorResponse("Item not found", 404);
            }

            // Check for duplicate name (if name is being changed)
            if (data.name && data.name !== existing.name) {
                if (await itemsService.isNameTaken(data.name, id)) {
                    return errorResponse("An item with this name already exists", 409);
                }
            }

            // Build update data
            const updateData: Partial<UpdateItemDTO> = {};
            if (data.name !== undefined) updateData.name = data.name;
            if (data.description !== undefined) updateData.description = data.description;
            if (data.rarity !== undefined) updateData.rarity = data.rarity;
            if (data.type !== undefined) updateData.type = data.type;
            if (data.price !== undefined) updateData.price = data.price ? BigInt(data.price) : null;
            if (data.iconUrl !== undefined) updateData.iconUrl = data.iconUrl;
            if (data.imageUrl !== undefined) updateData.imageUrl = data.imageUrl;
            if (data.usageData !== undefined) updateData.usageData = data.usageData;

            const updatedItem = await itemsService.updateItem(id, updateData);
            return jsonResponse({ success: true, item: updatedItem });
        }, "update item");
    }

    /**
     * @route DELETE /api/items/:id
     * @description Deletes an item and its associated asset file.
     * 
     * @param id - Item ID (numeric)
     * @response 204 - Item deleted (no content)
     * @response 404 - Item not found
     * @response 500 - Error deleting item
     */
    if (pathname.match(/^\/api\/items\/\d+$/) && method === "DELETE") {
        const id = parseIdFromPath(pathname);
        if (!id) return null;

        return withErrorHandling(async () => {
            const existing = await itemsService.getItemById(id);
            if (!existing) {
                return errorResponse("Item not found", 404);
            }

            await itemsService.deleteItem(id);

            // Try to delete associated asset file
            const assetPath = join(assetsDir, `${id}.png`);
            try {
                const assetFile = Bun.file(assetPath);
                if (await assetFile.exists()) {
                    const { unlink } = await import("node:fs/promises");
                    await unlink(assetPath);
                }
            } catch (e) {
                // Non-critical: log but don't fail
                const { logger } = await import("@shared/lib/logger");
                logger.warn("web", `Could not delete asset file for item ${id}`, e);
            }

            return new Response(null, { status: 204 });
        }, "delete item");
    }

    /**
     * @route POST /api/items/:id/icon
     * @description Uploads or replaces an item's icon image.
     * 
     * @param id - Item ID (numeric)
     * @body (Multipart) { image: File }
     * @response 200 - `{ success: true, item: Item }`
     * @response 400 - No image file or invalid format
     * @response 404 - Item not found
     * @response 500 - Error uploading icon
     */
    if (pathname.match(/^\/api\/items\/\d+\/icon$/) && method === "POST") {
        const id = parseInt(pathname.split("/")[3] || "0");
        if (!id) return null;

        return withErrorHandling(async () => {
            const existing = await itemsService.getItemById(id);
            if (!existing) {
                return errorResponse("Item not found", 404);
            }

            const formData = await req.formData();
            const imageFile = formData.get("image") as File | null;

            if (!imageFile) {
                return errorResponse("No image file provided", 400);
            }

            const buffer = await imageFile.arrayBuffer();
            const bytes = new Uint8Array(buffer);

            if (!validateImageFormat(bytes)) {
                return errorResponse("Invalid image file. Only PNG, JPEG, WebP, and GIF are allowed.", 400);
            }

            if (buffer.byteLength > MAX_IMAGE_SIZE) {
                return errorResponse("Image file too large. Maximum size is 15MB.", 400);
            }

            const fileName = `${id}.png`;
            const filePath = join(assetsDir, fileName);
            await Bun.write(filePath, buffer);

            const assetUrl = `/assets/items/${fileName}?v=${Date.now()}`;
            const updatedItem = await itemsService.updateItem(id, {
                iconUrl: assetUrl,
                imageUrl: assetUrl,
            });

            return jsonResponse({ success: true, item: updatedItem });
        }, "upload item icon");
    }

    return null;
}

export const itemsRoutes: RouteModule = {
    name: "items",
    handler
};
