package service

import (
	"context"
	"fmt"

	v1 "template-plus/api/v1"
	"template-plus/api/v1/apperrors"
	"template-plus/internal/repository"
)

type MenuService interface {
	Select(ctx context.Context) ([]v1.MenuListResponse, error)
	ID(ctx context.Context, id int64) (*v1.SimpleMenuResponse, error)
	Create(ctx context.Context, request *v1.CreateMenuRequest) error
	Update(ctx context.Context, request *v1.UpdateMenuRequest) error
	Delete(ctx context.Context, id int64) error
}

func NewMenuService(
	service *Service,
	menuRepo repository.MenuRepository,
	roleMenuRepo repository.RoleMenuRepository,
) MenuService {
	return &menuService{
		menuRepo:     menuRepo,
		roleMenuRepo: roleMenuRepo,
		Service:      service,
	}
}

type menuService struct {
	menuRepo     repository.MenuRepository
	roleMenuRepo repository.RoleMenuRepository
	*Service
}

func (m *menuService) Select(ctx context.Context) ([]v1.MenuListResponse, error) {
	menus, err := m.menuRepo.SelectMenus(ctx)
	if err != nil {
		return nil, apperrors.NewInternalServerError("failed to list menus", err)
	}

	return v1.MenuToMenuListResponse(menus), nil
}

func (m *menuService) ID(ctx context.Context, id int64) (*v1.SimpleMenuResponse, error) {
	menu, err := m.menuRepo.GetMenuByID(ctx, id)
	if err != nil {
		return nil, apperrors.NewInternalServerError("failed to retrieve menu", err)
	}
	if menu == nil {
		return nil, apperrors.NewNotFoundError(fmt.Sprintf("menu ID %d not found", id), err)
	}

	response := v1.MenuToSimpleMenuResponse(menu)
	return response, nil
}

func (m *menuService) Create(ctx context.Context, request *v1.CreateMenuRequest) error {
	menu := v1.CreateRequestToMenu(request)

	if _, err := m.menuRepo.InsertMenu(ctx, menu); err != nil {
		return apperrors.NewInternalServerError("failed to create menu", err)
	}
	return nil
}

func (m *menuService) Update(ctx context.Context, request *v1.UpdateMenuRequest) error {
	menu := v1.UpdateRequestToMenu(request)

	rowsAffected, err := m.menuRepo.UpdateMenu(ctx, menu)
	if err != nil {
		return apperrors.NewInternalServerError(fmt.Sprintf("failed to update menu with ID %d", menu.ID), err)
	}

	rows, _ := rowsAffected.RowsAffected()
	if rows <= 0 {
		return apperrors.NewNotFoundError(fmt.Sprintf("menu ID %d not found", menu.ID), nil)
	}
	return nil
}

func (m *menuService) Delete(ctx context.Context, id int64) error {
	number, err := m.menuRepo.GetChildrenMenuNumber(ctx, id)
	if err != nil {
		return apperrors.NewInternalServerError(fmt.Sprintf("failed to get children menu number by ID %d", id), err)
	}
	if number > 0 {
		return apperrors.NewInternalServerError(fmt.Sprintf("menu with ID %d has children menus", id), nil)
	}
	return m.tm.Transaction(ctx, func(ctx context.Context) error {
		if _, err := m.roleMenuRepo.DeleteRoleMenuByMenuID(ctx, id); err != nil {
			return apperrors.NewInternalServerError(fmt.Sprintf("failed to delete role menu by menu ID %d", id), err)
		}
		rowsAffected, err := m.menuRepo.DeleteMenu(ctx, id)
		if err != nil {
			return apperrors.NewInternalServerError(fmt.Sprintf("failed to delete menu with ID %d", id), err)
		}

		rows, _ := rowsAffected.RowsAffected()
		if rows <= 0 {
			return apperrors.NewNotFoundError(fmt.Sprintf("menu ID %d not found", id), nil)
		}
		return nil
	})
}
please optimize the code and add comments