package steps

import (
	"context"
	"fmt"

	"github.com/pingcap/test-infra/caselib/pkg/host"
	apiresource "github.com/pingcap/test-infra/common/model/resource"
	"github.com/pingcap/test-infra/sdk/core"
	"github.com/pingcap/test-infra/sdk/resource"
)

type dumpling struct {
	dumplingRes *apiresource.ResourceHandle
	mysqlRes    *apiresource.ResourceHandle
	opts        *host.DumplingGeneralOpts
}

func (d dumpling) String() string {
	return fmt.Sprintf("dumpling from resource %s with options `%v`", d.mysqlRes.Name, d.opts)
}

func (d dumpling) Execute(ctx context.Context) error {
	dumplingResolved, err := core.ResolveResourceHandle(d.dumplingRes)
	if err != nil {
		return err
	}
	dumplingHost := host.NewDumplingHost(dumplingResolved.(resource.WorkloadNode))
	mysqlResolved, err := core.ResolveResourceHandle(d.mysqlRes)
	if err != nil {
		return err
	}
	mysql := mysqlResolved.(resource.MySQLCompatible)
	mysqlUrl, err := mysql.ServiceURL(resource.DBAddr)
	if err != nil {
		return err
	}
	return dumplingHost.Dump(mysqlUrl, d.opts)
}

var _ Step = &dumpling{}

type tiupDumpling struct {
	dumplingRes *apiresource.ResourceHandle
	mysqlRes    *apiresource.ResourceHandle
	version     *string
	opts        *host.DumplingGeneralOpts
}

func (d *tiupDumpling) String() string {
	return fmt.Sprintf("tiup dumpling from resource %s with options `%v`", d.mysqlRes.Name, d.opts)
}

func (d *tiupDumpling) Execute(ctx context.Context) error {
	dumplingResolved, err := core.ResolveResourceHandle(d.dumplingRes)
	if err != nil {
		return err
	}
	dumplingHost := host.NewDumplingHost(dumplingResolved.(resource.WorkloadNode))
	mysqlResolved, err := core.ResolveResourceHandle(d.mysqlRes)
	if err != nil {
		return err
	}
	mysql := mysqlResolved.(resource.MySQLCompatible)
	mysqlUrl, err := mysql.ServiceURL(resource.DBAddr)
	if err != nil {
		return err
	}
	return dumplingHost.TiUPDump(mysqlUrl, *d.version, d.opts)
}

var _ Step = &tiupDumpling{}

func (b *Builder) Dumpling(dumplingRes, mysqlRes *apiresource.ResourceHandle, opts *host.DumplingGeneralOpts) *Builder {
	b.steps = append(b.steps, dumpling{dumplingRes: dumplingRes, mysqlRes: mysqlRes, opts: opts})
	return b
}

// NewDumpling for parallel
func NewDumpling(dumplingRes, mysqlRes *apiresource.ResourceHandle, opts *host.DumplingGeneralOpts) Step {
	return &dumpling{dumplingRes: dumplingRes, mysqlRes: mysqlRes, opts: opts}
}

func (b *Builder) TiUPDumpling(dumplingRes, mysqlRes *apiresource.ResourceHandle, version *string, opts *host.DumplingGeneralOpts) *Builder {
	b.steps = append(b.steps, &tiupDumpling{dumplingRes: dumplingRes, mysqlRes: mysqlRes, version: version, opts: opts})
	return b
}

func NewTiUPDumpling(dumplingRes, mysqlRes *apiresource.ResourceHandle, version *string, opts *host.DumplingGeneralOpts) Step {
	return &tiupDumpling{dumplingRes: dumplingRes, mysqlRes: mysqlRes, version: version, opts: opts}
}

type dumplingSnapshot struct {
	// dumplingRes *apiresource.ResourceHandle
	// mysqlRes    *apiresource.ResourceHandle
	// opts        *host.DumplingGeneralOpts
	dumpling
	snapshot *string
}

func (d dumplingSnapshot) String() string {
	return fmt.Sprintf("dumpling snapshot %s from resource %s with options `%v`", *d.snapshot, d.mysqlRes.Name, d.opts)
}

func (d dumplingSnapshot) Execute(ctx context.Context) error {
	dumplingResolved, err := core.ResolveResourceHandle(d.dumplingRes)
	if err != nil {
		return err
	}
	dumplingHost := host.NewDumplingHost(dumplingResolved.(resource.WorkloadNode))
	mysqlResolved, err := core.ResolveResourceHandle(d.mysqlRes)
	if err != nil {
		return err
	}
	mysql := mysqlResolved.(resource.MySQLCompatible)
	mysqlUrl, err := mysql.ServiceURL(resource.DBAddr)
	if err != nil {
		return err
	}
	d.opts.Options = fmt.Sprintf("%s --snapshot \"%s\"", d.opts.Options, *d.snapshot)
	return dumplingHost.Dump(mysqlUrl, d.opts)
}

func (b *Builder) DumplingSnapshot(dumplingRes, mysqlRes *apiresource.ResourceHandle, opts *host.DumplingGeneralOpts, snapshot *string) *Builder {
	dumpling := dumpling{dumplingRes: dumplingRes, mysqlRes: mysqlRes, opts: opts}
	b.steps = append(b.steps, dumplingSnapshot{dumpling: dumpling, snapshot: snapshot})
	return b
}
