package usecase

import (
	"backend/pkg/permission/domain"
	"backend/pkg/permission/domain/entity"
	"backend/pkg/permission/domain/permission"
	"backend/pkg/permission/domain/repository"
	"backend/pkg/permission/domain/usecase"
	"context"
	"fmt"
	"time"

	"go.mongodb.org/mongo-driver/v2/bson"
)

type RoleUseCaseConfig struct {
	AdminRoleUID string // 管理員角色 UID
	UIDPrefix    string // UID 前綴 (e.g., "ROLE")
	UIDLength    int    // UID 長度 (不含前綴)
}

type RoleUseCaseParam struct {
	RoleRepo        repository.RoleRepository
	UserRoleRepo    repository.UserRoleRepository
	RolePermUseCase usecase.RolePermissionUseCase
	Config          RoleUseCaseConfig
}

type roleUseCase struct {
	RoleUseCaseParam
}

// NewRoleUseCase 建立角色 UseCase
func NewRoleUseCase(param RoleUseCaseParam) usecase.RoleUseCase {
	// 設定預設值
	if param.Config.UIDPrefix == "" {
		param.Config.UIDPrefix = "ROLE"
	}
	if param.Config.UIDLength == 0 {
		param.Config.UIDLength = 10
	}

	return &roleUseCase{
		RoleUseCaseParam: param,
	}
}

func (uc *roleUseCase) Create(ctx context.Context, req usecase.CreateRoleRequest) (*usecase.RoleResponse, error) {
	// 生成 UID
	nextID, err := uc.RoleRepo.NextID(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to generate role id: %w", err)
	}

	uid := fmt.Sprintf("%s%0*d", uc.Config.UIDPrefix, uc.Config.UIDLength, nextID)

	// 建立角色
	role := &entity.Role{
		ID:       bson.NewObjectID(),
		UID:      uid,
		ClientID: req.ClientID,
		Name:     req.Name,
		Status:   domain.RecordActive,
	}
	role.CreateTime = time.Now().Unix()
	role.UpdateTime = role.CreateTime

	if err := uc.RoleRepo.Create(ctx, role); err != nil {
		return nil, err
	}

	// 設定權限
	if len(req.Permissions) > 0 && uc.RolePermUseCase != nil {
		if err := uc.RolePermUseCase.UpdateRolePermissions(ctx, uid, req.Permissions); err != nil {
			return nil, err
		}
	}

	// 查詢完整角色資訊
	return uc.Get(ctx, uid)
}

func (uc *roleUseCase) Update(ctx context.Context, uid string, req usecase.UpdateRoleRequest) (*usecase.RoleResponse, error) {
	// 檢查角色是否存在
	role, err := uc.RoleRepo.GetByUID(ctx, uid)
	if err != nil {
		return nil, err
	}

	// 更新欄位
	if req.Name != nil {
		role.Name = *req.Name
	}
	if req.Status != nil {
		role.Status = *req.Status
	}
	role.UpdateTime = time.Now().Unix()

	if err := uc.RoleRepo.Update(ctx, role); err != nil {
		return nil, err
	}

	// 更新權限
	if req.Permissions != nil && uc.RolePermUseCase != nil {
		if err := uc.RolePermUseCase.UpdateRolePermissions(ctx, uid, req.Permissions); err != nil {
			return nil, err
		}
	}

	return uc.Get(ctx, uid)
}

func (uc *roleUseCase) Delete(ctx context.Context, uid string) error {
	// 檢查角色是否存在
	_, err := uc.RoleRepo.GetByUID(ctx, uid)
	if err != nil {
		return err
	}

	// 檢查是否有使用者使用此角色
	users, err := uc.UserRoleRepo.GetByRoleID(ctx, uid)
	if err != nil {
		return err
	}

	if len(users) > 0 {
		return fmt.Errorf("role has %d users, cannot delete", len(users))
	}

	// 刪除角色
	return uc.RoleRepo.Delete(ctx, uid)
}

func (uc *roleUseCase) Get(ctx context.Context, uid string) (*usecase.RoleResponse, error) {
	role, err := uc.RoleRepo.GetByUID(ctx, uid)
	if err != nil {
		return nil, err
	}

	// 取得權限
	var permissions permission.Permissions
	if uc.RolePermUseCase != nil {
		permissions, _ = uc.RolePermUseCase.GetByRoleUID(ctx, uid)
	}
	if permissions == nil {
		permissions = make(permission.Permissions)
	}

	return uc.toResponse(role, permissions), nil
}

func (uc *roleUseCase) List(ctx context.Context, filter usecase.RoleFilterRequest) ([]*usecase.RoleResponse, error) {
	repoFilter := repository.RoleFilter{
		ClientID: filter.ClientID,
		Name:     filter.Name,
		Status:   filter.Status,
	}

	roles, err := uc.RoleRepo.List(ctx, repoFilter)
	if err != nil {
		return nil, err
	}

	return uc.toResponseList(ctx, roles, filter.Permissions), nil
}

func (uc *roleUseCase) Page(ctx context.Context, filter usecase.RoleFilterRequest, page, size int) (*usecase.RolePageResponse, error) {
	repoFilter := repository.RoleFilter{
		ClientID: filter.ClientID,
		Name:     filter.Name,
		Status:   filter.Status,
	}

	roles, total, err := uc.RoleRepo.Page(ctx, repoFilter, page, size)
	if err != nil {
		return nil, err
	}

	// 取得所有角色的使用者數量 (批量查詢，避免 N+1)
	roleUIDs := make([]string, len(roles))
	for i, role := range roles {
		roleUIDs[i] = role.UID
	}

	userCounts, err := uc.UserRoleRepo.CountByRoleID(ctx, roleUIDs)
	if err != nil {
		return nil, err
	}

	// 組裝回應
	list := make([]*usecase.RoleWithUserCountResponse, 0, len(roles))
	for _, role := range roles {
		// 取得權限
		var permissions permission.Permissions
		if uc.RolePermUseCase != nil {
			permissions, _ = uc.RolePermUseCase.GetByRoleUID(ctx, role.UID)
		}
		if permissions == nil {
			permissions = make(permission.Permissions)
		}

		// 權限過濾 (如果有指定)
		if len(filter.Permissions) > 0 {
			hasPermission := false
			for _, reqPerm := range filter.Permissions {
				if permissions.HasPermission(reqPerm) {
					hasPermission = true
					break
				}
			}
			if !hasPermission {
				continue
			}
		}

		resp := &usecase.RoleWithUserCountResponse{
			RoleResponse: *uc.toResponse(role, permissions),
			UserCount:    userCounts[role.UID],
		}
		list = append(list, resp)
	}

	return &usecase.RolePageResponse{
		List:  list,
		Total: total,
		Page:  page,
		Size:  size,
	}, nil
}

func (uc *roleUseCase) toResponse(role *entity.Role, permissions permission.Permissions) *usecase.RoleResponse {
	if permissions == nil {
		permissions = make(permission.Permissions)
	}

	return &usecase.RoleResponse{
		ID:          role.ID.Hex(),
		UID:         role.UID,
		ClientID:    role.ClientID,
		Name:        role.Name,
		Status:      role.Status,
		Permissions: permissions,
		CreateTime:  time.Unix(role.CreateTime, 0).UTC().Format(time.RFC3339),
		UpdateTime:  time.Unix(role.UpdateTime, 0).UTC().Format(time.RFC3339),
	}
}

func (uc *roleUseCase) toResponseList(ctx context.Context, roles []*entity.Role, permFilter []string) []*usecase.RoleResponse {
	result := make([]*usecase.RoleResponse, 0, len(roles))

	for _, role := range roles {
		var permissions permission.Permissions
		if uc.RolePermUseCase != nil {
			permissions, _ = uc.RolePermUseCase.GetByRoleUID(ctx, role.UID)
		}
		if permissions == nil {
			permissions = make(permission.Permissions)
		}

		// 權限過濾
		if len(permFilter) > 0 {
			hasPermission := false
			for _, reqPerm := range permFilter {
				if permissions.HasPermission(reqPerm) {
					hasPermission = true
					break
				}
			}
			if !hasPermission {
				continue
			}
		}

		result = append(result, uc.toResponse(role, permissions))
	}

	return result
}
