package cmdpacks

import (
	"context"

	"github.com/pkg/errors"
	"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/models"
	"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/services/tables"
)

type tablePVar struct {
	state          *controllers.State
	tableService   *tables.Service
	readController *controllers.TableReadController
}

func (rs tablePVar) Get(ctx context.Context) (any, error) {
	return newTableProxy(rs.state.ResultSet().TableInfo), nil
}

func (rs tablePVar) Set(ctx context.Context, value any) error {
	scanNewTable := func(name string) error {
		tableInfo, err := rs.tableService.Describe(ctx, name)
		if err != nil {
			return errors.Wrapf(err, "cannot describe %v", name)
		}

		resultSet, err := rs.tableService.Scan(ctx, tableInfo)
		if resultSet != nil {
			resultSet = rs.tableService.Filter(resultSet, rs.state.Filter())
		}

		msg := rs.readController.SetResultSet(resultSet)
		commandctrl.PostMsg(ctx, msg)
		return nil
	}

	tblVal, ok := value.(SimpleProxy[*models.TableInfo])
	if ok {
		return scanNewTable(tblVal.value.Name)
	}

	strVal, ok := value.(string)
	if ok {
		return scanNewTable(strVal)
	}
	return errors.New("new value to @table is not a table name")
}

type resultSetPVar struct {
	state          *controllers.State
	readController *controllers.TableReadController
}

func (rs resultSetPVar) Get(ctx context.Context) (any, error) {
	return newResultSetProxy(rs.state.ResultSet()), nil
}

func (rs resultSetPVar) Set(ctx context.Context, value any) error {
	rsVal, ok := value.(SimpleProxy[*models.ResultSet])
	if !ok {
		return errors.New("new value to @resultset is nil or not a result set")
	}

	msg := rs.readController.SetResultSet(rsVal.value)
	commandctrl.PostMsg(ctx, msg)
	return nil
}

type markedSetPVar struct {
	state          *controllers.State
	readController *controllers.TableReadController
}

func (rs markedSetPVar) Get(ctx context.Context) (any, error) {
	return resultSetMarkedItemsProxy{rs.state.ResultSet()}, nil
}

type itemPVar struct {
	state *controllers.State
}

func (rs itemPVar) Get(ctx context.Context) (any, error) {
	selItem, ok := commandctrl.SelectedItemIndex(ctx)
	if !ok {
		return nil, nil
	}
	rset := rs.state.ResultSet()
	if selItem < 0 || selItem >= len(rs.state.ResultSet().Items()) {
		return nil, nil
	}

	return itemProxy{rset, selItem, rset.Items()[selItem]}, nil
}

func (rs itemPVar) Set(ctx context.Context, value any) error {
	rsVal, ok := value.(itemProxy)
	if !ok {
		return errors.New("new value to @item is not an item")
	}

	if msg := commandctrl.SetSelectedItemIndex(ctx, rsVal.idx); msg != nil {
		commandctrl.PostMsg(ctx, msg)
	}

	return nil
}
