package usecase

import (
	"context"
	"sort"
	"strings"

	memberentity "haixun-backend/internal/model/member/domain/entity"
	"haixun-backend/internal/model/permission/domain/entity"
	domrepo "haixun-backend/internal/model/permission/domain/repository"
	domusecase "haixun-backend/internal/model/permission/domain/usecase"
)

type permissionUseCase struct {
	permissions     domrepo.PermissionRepository
	rolePermissions domrepo.RolePermissionRepository
}

func NewUseCase(permissions domrepo.PermissionRepository, rolePermissions domrepo.RolePermissionRepository) domusecase.UseCase {
	return &permissionUseCase{permissions: permissions, rolePermissions: rolePermissions}
}

func (u *permissionUseCase) EnsureDefaultPermissions(ctx context.Context) error {
	for _, permission := range defaultPermissions() {
		if err := u.permissions.UpsertByName(ctx, permission); err != nil {
			return err
		}
	}
	return nil
}

func (u *permissionUseCase) EnsureDefaultRolePermissions(ctx context.Context, tenantID string) error {
	if tenantID == "" {
		return nil
	}
	perms, err := u.permissions.List(ctx, domrepo.PermissionFilter{Status: entity.StatusOpen})
	if err != nil {
		return err
	}
	allIDs := make([]string, 0, len(perms))
	byName := make(map[string]string, len(perms))
	for _, item := range perms {
		id := item.ID.Hex()
		allIDs = append(allIDs, id)
		byName[item.Name] = id
	}
	if err := u.rolePermissions.SetForRole(ctx, tenantID, "admin", allIDs); err != nil {
		return err
	}
	userIDs := make([]string, 0, len(defaultUserPermissionNames()))
	for _, name := range defaultUserPermissionNames() {
		if id, ok := byName[name]; ok {
			userIDs = append(userIDs, id)
		}
	}
	return u.rolePermissions.SetForRole(ctx, tenantID, "user", userIDs)
}

func (u *permissionUseCase) Catalog(ctx context.Context, req domusecase.CatalogRequest) ([]domusecase.PermissionNode, []domusecase.PermissionNode, error) {
	filter := domrepo.PermissionFilter{}
	if req.Status != "" {
		filter.Status = entity.Status(req.Status)
	}
	if req.Type != "" {
		filter.Type = entity.Type(req.Type)
	}
	items, err := u.permissions.List(ctx, filter)
	if err != nil {
		return nil, nil, err
	}
	list := permissionNodes(items)
	if req.Tree {
		return buildTree(list), nil, nil
	}
	return nil, list, nil
}

func (u *permissionUseCase) Me(ctx context.Context, member *memberentity.Member, includeTree bool) (*domusecase.MePermissions, error) {
	if member == nil {
		return nil, nil
	}
	roles := member.Roles
	if len(roles) == 0 {
		roles = []string{"user"}
	}
	rolePermissions, err := u.rolePermissions.ListByRoles(ctx, member.TenantID, roles)
	if err != nil {
		return nil, err
	}
	ids := make([]string, 0, len(rolePermissions))
	for _, item := range rolePermissions {
		ids = append(ids, item.PermissionID)
	}
	perms, err := u.permissions.ListByIDs(ctx, ids)
	if err != nil {
		return nil, err
	}
	if len(perms) == 0 {
		perms, err = u.permissions.List(ctx, domrepo.PermissionFilter{Status: entity.StatusOpen})
		if err != nil {
			return nil, err
		}
		if !hasRole(roles, "admin") {
			perms = filterPermissionsByName(perms, defaultUserPermissionNames())
		}
	}
	nodes := permissionNodes(perms)
	result := &domusecase.MePermissions{
		TenantID:    member.TenantID,
		UID:         member.UID,
		Roles:       roles,
		Permissions: map[string]string{},
	}
	for _, node := range nodes {
		if node.HTTPPath == "" || node.HTTPMethods == "" {
			continue
		}
		if existing, ok := result.Permissions[node.HTTPPath]; ok {
			result.Permissions[node.HTTPPath] = mergeHTTPMethods(existing, node.HTTPMethods)
			continue
		}
		result.Permissions[node.HTTPPath] = node.HTTPMethods
	}
	if includeTree {
		result.Tree = buildTree(nodes)
	}
	return result, nil
}

func mergeHTTPMethods(existing, next string) string {
	seen := map[string]struct{}{}
	out := make([]string, 0, 8)
	for _, block := range []string{existing, next} {
		for _, item := range strings.Split(block, "|") {
			method := strings.ToUpper(strings.TrimSpace(item))
			if method == "" {
				continue
			}
			if _, ok := seen[method]; ok {
				continue
			}
			seen[method] = struct{}{}
			out = append(out, method)
		}
	}
	return strings.Join(out, "|")
}

func hasRole(roles []string, role string) bool {
	for _, item := range roles {
		if item == role {
			return true
		}
	}
	return false
}

func filterPermissionsByName(items []*entity.Permission, names []string) []*entity.Permission {
	allowed := map[string]struct{}{}
	for _, name := range names {
		allowed[name] = struct{}{}
	}
	out := make([]*entity.Permission, 0, len(items))
	for _, item := range items {
		if _, ok := allowed[item.Name]; ok {
			out = append(out, item)
		}
	}
	return out
}

func defaultUserPermissionNames() []string {
	return []string{
		"auth.logout",
		"member.me.read",
		"member.me.update",
		"member.placement_settings.read",
		"member.placement_settings.update",
		"permission.me.read",
		"setting.manage",
		"ai.use",
		"brand.manage",
		"persona.manage",
		"placement_topic.manage",
		"threads_account.manage",
		"job.manage",
		"job.schedule.manage",
	}
}

func (u *permissionUseCase) ReplaceRolePermissions(ctx context.Context, tenantID, roleKey string, permissionIDs []string) error {
	return u.rolePermissions.SetForRole(ctx, tenantID, roleKey, permissionIDs)
}

func permissionNodes(items []*entity.Permission) []domusecase.PermissionNode {
	out := make([]domusecase.PermissionNode, 0, len(items))
	for _, item := range items {
		out = append(out, domusecase.PermissionNode{
			ID:          item.ID.Hex(),
			Parent:      item.Parent,
			Name:        item.Name,
			HTTPMethods: item.HTTPMethods,
			HTTPPath:    item.HTTPPath,
			Status:      string(item.Status),
			Type:        string(item.Type),
		})
	}
	sort.SliceStable(out, func(i, j int) bool { return out[i].Name < out[j].Name })
	return out
}

func buildTree(items []domusecase.PermissionNode) []domusecase.PermissionNode {
	byParent := map[string][]domusecase.PermissionNode{}
	index := map[string]*domusecase.PermissionNode{}
	nodes := make([]domusecase.PermissionNode, len(items))
	copy(nodes, items)
	for i := range nodes {
		index[nodes[i].ID] = &nodes[i]
		byParent[nodes[i].Parent] = append(byParent[nodes[i].Parent], nodes[i])
	}
	for parent, children := range byParent {
		sort.SliceStable(children, func(i, j int) bool { return children[i].Name < children[j].Name })
		byParent[parent] = children
	}
	for parent, children := range byParent {
		if node, ok := index[parent]; ok {
			node.Children = children
		}
	}
	return byParent[""]
}

func defaultPermissions() []*entity.Permission {
	allMethods := "GET|POST|PUT|PATCH|DELETE"
	return []*entity.Permission{
		{Name: "auth.logout", HTTPMethods: "POST", HTTPPath: "/api/v1/auth/logout", Status: entity.StatusOpen, Type: entity.TypeFrontendUser},

		{Name: "member.me.read", HTTPMethods: "GET", HTTPPath: "/api/v1/members/me", Status: entity.StatusOpen, Type: entity.TypeFrontendUser},
		{Name: "member.me.update", HTTPMethods: "PATCH", HTTPPath: "/api/v1/members/me", Status: entity.StatusOpen, Type: entity.TypeFrontendUser},
		{Name: "member.placement_settings.read", HTTPMethods: "GET", HTTPPath: "/api/v1/members/me/placement-settings", Status: entity.StatusOpen, Type: entity.TypeFrontendUser},
		{Name: "member.placement_settings.update", HTTPMethods: "PATCH", HTTPPath: "/api/v1/members/me/placement-settings", Status: entity.StatusOpen, Type: entity.TypeFrontendUser},

		{Name: "permission.catalog.read", HTTPMethods: "GET", HTTPPath: "/api/v1/permissions/catalog", Status: entity.StatusOpen, Type: entity.TypeBackendUser},
		{Name: "permission.me.read", HTTPMethods: "GET", HTTPPath: "/api/v1/permissions/me", Status: entity.StatusOpen, Type: entity.TypeFrontendUser},

		{Name: "setting.manage", HTTPMethods: "GET|PUT|DELETE", HTTPPath: "/api/v1/settings/*", Status: entity.StatusOpen, Type: entity.TypeBackendUser},
		{Name: "ai.use", HTTPMethods: "GET|POST", HTTPPath: "/api/v1/ai/*", Status: entity.StatusOpen, Type: entity.TypeFrontendUser},

		{Name: "brand.manage", HTTPMethods: allMethods, HTTPPath: "/api/v1/brands/*", Status: entity.StatusOpen, Type: entity.TypeBackendUser},
		{Name: "persona.manage", HTTPMethods: allMethods, HTTPPath: "/api/v1/personas/*", Status: entity.StatusOpen, Type: entity.TypeBackendUser},
		{Name: "placement_topic.manage", HTTPMethods: allMethods, HTTPPath: "/api/v1/placement/topics/*", Status: entity.StatusOpen, Type: entity.TypeBackendUser},
		{Name: "threads_account.manage", HTTPMethods: "GET|POST|PUT|PATCH", HTTPPath: "/api/v1/threads-accounts/*", Status: entity.StatusOpen, Type: entity.TypeBackendUser},

		{Name: "job.manage", HTTPMethods: "GET|POST", HTTPPath: "/api/v1/jobs/*", Status: entity.StatusOpen, Type: entity.TypeBackendUser},
		{Name: "job.schedule.manage", HTTPMethods: "GET|POST|PUT|DELETE", HTTPPath: "/api/v1/job/schedules/*", Status: entity.StatusOpen, Type: entity.TypeBackendUser},
		{Name: "job.template.manage", HTTPMethods: "GET|PUT", HTTPPath: "/api/v1/job/templates/*", Status: entity.StatusOpen, Type: entity.TypeBackendUser},
	}
}
