package applogic

import (
	"context"
	"fmt"
	"log"
	"strconv"

	"aijianzhan/platform/internal/authx"
	"aijianzhan/platform/internal/crud"
	"aijianzhan/platform/internal/meta"
	"aijianzhan/platform/internal/svc"
	"aijianzhan/platform/internal/types"
)

type CrudLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewCrudLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CrudLogic {
	return &CrudLogic{ctx: ctx, svcCtx: svcCtx}
}

func (l *CrudLogic) withOrgScope() context.Context {
	ctx := l.ctx
	role := authx.Role(ctx)
	orgID := authx.OrgUnitID(ctx)
	scope := crud.RowScope{WriteOrgUnit: orgID}
	// owner / agent 不按组织裁剪；其它角色若绑定了组织则只看本组织及下级
	if !authx.IsCompanyAdmin(role) && role != authx.Role智能体 && orgID > 0 && l.svcCtx.OrgUnits != nil {
		ids, err := l.svcCtx.OrgUnits.DescendantIDs(ctx, authx.TenantID(ctx), orgID)
		if err == nil {
			scope.OrgUnitIDs = ids
		} else {
			scope.OrgUnitIDs = []int64{orgID}
		}
	}
	return crud.WithRowScope(ctx, scope)
}

func (l *CrudLogic) List(slug, resource string, page, pageSize int, filters map[string]string, sortBy string) (*types.PageResult, error) {
	ctx := l.withOrgScope()
	tenantID := authx.TenantID(ctx)
	ref, err := l.resolveResource(ctx, tenantID, slug, resource, "list")
	if err != nil {
		return nil, err
	}
	items, total, err := l.svcCtx.CRUD.List(ctx, ref, tenantID, page, pageSize, filters, sortBy)
	if err != nil {
		return nil, err
	}
	if page <= 0 {
		page = 1
	}
	if pageSize <= 0 {
		pageSize = 20
	}
	return &types.PageResult{Items: items, Page: page, PageSize: pageSize, Total: total}, nil
}

func (l *CrudLogic) Get(slug, resource, id string) (map[string]any, error) {
	ctx := l.withOrgScope()
	tenantID := authx.TenantID(ctx)
	ref, err := l.resolveResource(ctx, tenantID, slug, resource, "get")
	if err != nil {
		return nil, err
	}
	return l.svcCtx.CRUD.Get(ctx, ref, tenantID, id)
}

func (l *CrudLogic) Create(slug, resource string, body map[string]any) (map[string]any, error) {
	ctx := l.withOrgScope()
	tenantID := authx.TenantID(ctx)
	userID := authx.UserID(ctx)
	ref, err := l.resolveResource(ctx, tenantID, slug, resource, "create")
	if err != nil {
		return nil, err
	}
	return l.svcCtx.CRUD.Create(ctx, ref, tenantID, userID, body)
}

func (l *CrudLogic) Update(slug, resource, id string, body map[string]any) (map[string]any, error) {
	ctx := l.withOrgScope()
	tenantID := authx.TenantID(ctx)
	ref, err := l.resolveResource(ctx, tenantID, slug, resource, "update")
	if err != nil {
		return nil, err
	}
	return l.svcCtx.CRUD.Update(ctx, ref, tenantID, id, body)
}

func (l *CrudLogic) Delete(slug, resource, id string) error {
	ctx := l.withOrgScope()
	tenantID := authx.TenantID(ctx)
	ref, err := l.resolveResource(ctx, tenantID, slug, resource, "delete")
	if err != nil {
		return err
	}
	return l.svcCtx.CRUD.Delete(ctx, ref, tenantID, id)
}

func (l *CrudLogic) resolveResource(ctx context.Context, tenantID int64, slug, resource, operation string) (*meta.ResourceRef, error) {
	ref, err := l.svcCtx.Meta.ResolveResource(ctx, tenantID, slug, resource)
	if err != nil {
		return nil, err
	}
	if ref == nil || ref.App == nil || ref.App.Slug != slug {
		return nil, fmt.Errorf("resource isolation mismatch for app %s", slug)
	}
	if ref.App.SchemaName == "" || ref.Entity.Table == "" {
		return nil, fmt.Errorf("resource storage mapping missing for app %s", slug)
	}
	log.Printf(
		"app_resource op=%s tenant=%d requested_slug=%s resource=%s resolved_schema=%s resolved_table=%s blueprint_revision=%s",
		operation, tenantID, slug, resource, ref.App.SchemaName, ref.Entity.Table, ref.App.Blueprint.Revision(),
	)
	return ref, nil
}

func (l *CrudLogic) GetBlueprint(slug string) (any, error) {
	tenantID := authx.TenantID(l.ctx)
	app, err := l.svcCtx.Meta.GetBySlug(l.ctx, tenantID, slug)
	if err != nil {
		return nil, err
	}
	if app.Blueprint == nil {
		return nil, fmt.Errorf("blueprint missing")
	}
	return app.Blueprint, nil
}

func ParsePage(pageStr, sizeStr string) (int, int) {
	page, _ := strconv.Atoi(pageStr)
	size, _ := strconv.Atoi(sizeStr)
	if page <= 0 {
		page = 1
	}
	if size <= 0 {
		size = 20
	}
	return page, size
}
