package cmdpacks

import (
	"context"
	"time"

	"github.com/go-co-op/gocron/v2"
	"lmika.dev/cmd/dynamo-browse/internal/common/ui/commandctrl"
	"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/controllers"
	"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/services/tables"
	"ucl.lmika.dev/ucl"
)

type asyncModule struct {
	tableService *tables.Service
	state        *controllers.State
}

func (m asyncModule) asyncDo(ctx context.Context, args ucl.CallArgs) (any, error) {
	var block ucl.Invokable
	if err := args.Bind(&block); err != nil {
		return nil, err
	}

	return nil, commandctrl.ScheduleTask(ctx, func(ctx context.Context) error {
		_, err := block.Invoke(ctx)
		return err
	})
}

func (m asyncModule) asyncIn(ctx context.Context, args ucl.CallArgs) (any, error) {
	var (
		duration int
		block    ucl.Invokable
	)
	if err := args.Bind(&duration, &block); err != nil {
		return nil, err
	}

	_, err := commandctrl.CronScheduler(ctx).NewJob(
		gocron.OneTimeJob(
			gocron.OneTimeJobStartDateTime(time.Now().Add(time.Duration(duration)*time.Second)),
		),
		gocron.NewTask(func(ctx context.Context) {
			commandctrl.ScheduleTask(ctx, func(ctx context.Context) error {
				_, err := block.Invoke(ctx)
				return err
			})
		}),
		gocron.WithContext(ctx),
	)
	return nil, err
}

func (m asyncModule) asyncQuery(ctx context.Context, args ucl.CallArgs) (any, error) {
	var (
		block ucl.Invokable
	)

	args, q, tableInfo, err := parseQuery(ctx, args, m.state.ResultSet(), m.tableService, 1)
	if err != nil {
		return nil, err
	}

	if err := args.Bind(&block); err != nil {
		return nil, err
	}

	return nil, commandctrl.ScheduleAuxTask(ctx, "query: "+q.String(), func(ctx context.Context) error {
		newResultSet, err := m.tableService.ScanOrQuery(context.Background(), tableInfo, q, nil)
		if err != nil {
			return err
		}

		return commandctrl.ScheduleTask(ctx, func(ctx context.Context) error {
			_, err := block.Invoke(ctx, newResultSetProxy(newResultSet))
			return err
		})
	})
}

func moduleAsync(tableService *tables.Service, state *controllers.State) ucl.Module {
	m := asyncModule{
		state:        state,
		tableService: tableService,
	}

	return ucl.Module{
		Name: "async",
		Builtins: map[string]ucl.BuiltinHandler{
			"do":    m.asyncDo,
			"in":    m.asyncIn,
			"query": m.asyncQuery,
		},
	}
}
