import { v4 as uuidv4 } from 'uuid';
import { db } from '../application/database';
import { ResponseError } from '../error/response-error';
import {
  MenuItemResponse,
  toMenuItemResponse,
  CreateMenuItemRequest,
  UpdateMenuItemRequest,
  MenuItem,
} from '../model/menu-item-model';
import { MenuItemValidation } from '../validation/menu-item-validation';
import { Validation } from '../validation/validation';

export class MenuItemService {
  static async create(request: CreateMenuItemRequest): Promise<MenuItemResponse> {
    // Validate the create request
    const createRequest = Validation.validate(MenuItemValidation.CREATE, request);

    // Generate a UUID for the new menu item
    const menuItemId = uuidv4();

    // Insert the new menu item into the database with the UUID
    await db.query('INSERT INTO menu_item (id, name, url) VALUES (?, ?, ?)', [
      menuItemId,
      createRequest.name,
      createRequest.url,
    ]);

    // Retrieve the newly created menu item by the UUID
    const menuItem = await db.queryOne<MenuItem>('SELECT * FROM menu_item WHERE id = ? LIMIT 1', [menuItemId]);

    if (!menuItem) {
      throw new ResponseError(500, 'Failed to create menu item');
    }

    return toMenuItemResponse(menuItem);
  }

  static async getAll(): Promise<MenuItemResponse[]> {
    // Retrieve all menu items from the database
    const menuItems = await db.query<MenuItem>('SELECT * FROM menu_item ORDER BY created_at ASC');

    // Map each menu item to the response format
    return menuItems.map(toMenuItemResponse);
  }

  static async get(id: string): Promise<MenuItemResponse> {
    // Retrieve a single menu item by ID
    const menuItem = await db.queryOne<MenuItem>('SELECT * FROM menu_item WHERE id = ? LIMIT 1', [id]);

    if (!menuItem) {
      throw new ResponseError(404, 'Menu item not found');
    }

    return toMenuItemResponse(menuItem);
  }

  static async update(id: string, request: UpdateMenuItemRequest): Promise<MenuItemResponse> {
    // Check if the menu item exists
    await this.#checkMenuItemExist(id);

    // Validate the update request
    const updateRequest = Validation.validate(MenuItemValidation.UPDATE, request);

    // Update the menu item in the database
    await db.query('UPDATE menu_item SET name = ?, url = ? WHERE id = ?', [updateRequest.name, updateRequest.url, id]);

    // Retrieve the updated menu item by the ID
    const menuItem = await db.queryOne<MenuItem>('SELECT * FROM menu_item WHERE id = ? LIMIT 1', [id]);

    if (!menuItem) {
      throw new ResponseError(500, 'Failed to update menu item');
    }

    return toMenuItemResponse(menuItem);
  }

  static async updateVisibility(id: string, visible: boolean): Promise<MenuItemResponse> {
    // Check if the menu item exists
    await this.#checkMenuItemExist(id);

    // Update the visibility of the menu item in the database
    await db.query('UPDATE menu_item SET visible = ? WHERE id = ?', [visible, id]);

    // Retrieve the updated menu item by ID
    const menuItem = await db.queryOne<MenuItem>('SELECT * FROM menu_item WHERE id = ? LIMIT 1', [id]);

    if (!menuItem) {
      throw new ResponseError(500, 'Failed to update menu item visibility');
    }

    return toMenuItemResponse(menuItem);
  }

  static async delete(id: string): Promise<void> {
    // Check if the menu item exists
    await this.#checkMenuItemExist(id);

    // Delete the menu item from the database
    await db.query('DELETE FROM menu_item WHERE id = ?', [id]);
  }

  static async #checkMenuItemExist(id: string): Promise<void> {
    // Check if the menu item exists
    const existingMenuItem = await db.queryOne<{ id: string }>('SELECT id FROM menu_item WHERE id = ? LIMIT 1', [id]);

    if (!existingMenuItem) {
      throw new ResponseError(404, 'Menu item not found');
    }
  }
}
