package task

import (
	"context"
	"fmt"
	"io/fs"
	"os"

	"cuelang.org/go/cue"
	bk "github.com/moby/buildkit/client"
	"github.com/rs/zerolog/log"
	"go.dagger.io/dagger/compiler"
	"go.dagger.io/dagger/plancontext"
	"go.dagger.io/dagger/solver"
)

func init() {
	Register("ClientFilesystemWrite", func() Task { return &clientFilesystemWriteTask{} })
}

type clientFilesystemWriteTask struct {
}

func (t clientFilesystemWriteTask) Run(ctx context.Context, pctx *plancontext.Context, s *solver.Solver, v *compiler.Value) (*compiler.Value, error) {
	path, err := v.Lookup("path").String()
	if err != nil {
		return nil, err
	}

	path, err = clientFilePath(path)
	if err != nil {
		return nil, err
	}

	if err := t.writeContents(ctx, pctx, s, v, path); err != nil {
		return nil, err
	}

	return compiler.NewValue(), nil
}

func (t clientFilesystemWriteTask) writeContents(ctx context.Context, pctx *plancontext.Context, s *solver.Solver, v *compiler.Value, path string) error {
	lg := log.Ctx(ctx)
	contents := v.Lookup("contents")

	if plancontext.IsFSValue(contents) {
		lg.Debug().Str("path", path).Msg("writing files to local directory")
		return t.writeFS(ctx, pctx, s, contents, path)
	}

	permissions := fs.FileMode(0644) // default permission
	if vl := v.Lookup("permissions"); vl.Exists() {
		p, err := vl.Int64()
		if err != nil {
			return err
		}
		permissions = fs.FileMode(p)
	}

	if plancontext.IsSecretValue(contents) {
		lg.Debug().Str("path", path).Msg("writing secret to local file")
		secret, err := pctx.Secrets.FromValue(contents)
		if err != nil {
			return err
		}
		return os.WriteFile(path, []byte(secret.PlainText()), permissions)
	}

	k := contents.Kind()
	if k == cue.StringKind {
		lg.Debug().Str("path", path).Msg("writing to local file")
		text, err := contents.String()
		if err != nil {
			return err
		}
		return os.WriteFile(path, []byte(text), permissions)
	}

	return fmt.Errorf("unsupported type %q", k)
}

func (t clientFilesystemWriteTask) writeFS(ctx context.Context, pctx *plancontext.Context, s *solver.Solver, v *compiler.Value, path string) error {
	contents, err := pctx.FS.FromValue(v)
	if err != nil {
		return err
	}

	st, err := contents.State()
	if err != nil {
		return err
	}

	_, err = s.Export(ctx, st, nil, bk.ExportEntry{
		Type:      bk.ExporterLocal,
		OutputDir: path,
	}, pctx.Platform.Get())

	return err
}