package bootstrap_test

import (
	"context"
	"fmt"
	"net"
	"os"
	"runtime"
	"testing"
	"time"

	toki "git.toki-labs.com/toki/proto-socket/go"
	"go.uber.org/fx"
	"google.golang.org/protobuf/proto"
	"google.golang.org/protobuf/types/known/structpb"

	"iop/apps/node/internal/bootstrap"
	"iop/packages/go/config"
	iop "iop/proto/gen/iop"
)

func bootstrapEdgeParserMap() toki.ParserMap {
	return toki.ParserMap{
		toki.TypeNameOf(&iop.RegisterRequest{}): func(b []byte) (proto.Message, error) {
			m := &iop.RegisterRequest{}
			return m, proto.Unmarshal(b, m)
		},
	}
}

func freeAddrForBootstrap(t *testing.T) (host string, port int, addr string) {
	t.Helper()
	l, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatalf("listen: %v", err)
	}
	addr = l.Addr().String()
	l.Close()
	h, portStr, _ := net.SplitHostPort(addr)
	fmt.Sscanf(portStr, "%d", &port)
	return h, port, addr
}

// TestModuleDoesNotStartAdaptersBeforeStoreReady verifies that reg.Start is
// invoked only after the store is initialised. If storeDSN fails (workspace_root
// is an existing file, not a directory), the CLI persistent process must not
// have been started — the marker file must not exist.
func TestModuleDoesNotStartAdaptersBeforeStoreReady(t *testing.T) {
	if runtime.GOOS == "windows" {
		t.Skip("Unix shell required")
	}

	// A regular file used as workspace root → os.MkdirAll fails.
	wsFile, err := os.CreateTemp("", "iop-ws-*")
	if err != nil {
		t.Fatalf("create temp file: %v", err)
	}
	wsFile.Close()
	t.Cleanup(func() { os.Remove(wsFile.Name()) })

	markerPath := wsFile.Name() + ".marker"
	t.Cleanup(func() { os.Remove(markerPath) })

	settings, err := structpb.NewStruct(map[string]any{
		"profiles": map[string]any{
			"marker-profile": map[string]any{
				"command":                 "sh",
				"args":                    []any{"-c", fmt.Sprintf(`touch "%s"; sleep 30`, markerPath)},
				"env":                     []any{},
				"persistent":              true,
				"terminal":                false,
				"startup_idle_timeout_ms": float64(50),
			},
		},
	})
	if err != nil {
		t.Fatalf("build settings: %v", err)
	}

	host, port, addr := freeAddrForBootstrap(t)

	serverCtx, serverCancel := context.WithTimeout(context.Background(), 5*time.Second)
	t.Cleanup(serverCancel)

	server := toki.NewTcpServer(host, port, func(conn net.Conn) *toki.TcpClient {
		client := toki.NewTcpClient(conn, 30, 10, bootstrapEdgeParserMap())
		toki.AddRequestListenerTyped[*iop.RegisterRequest, *iop.RegisterResponse](
			&client.Communicator,
			func(_ *iop.RegisterRequest) (*iop.RegisterResponse, error) {
				return &iop.RegisterResponse{
					Accepted: true,
					NodeId:   "bootstrap-test-node",
					Alias:    "test",
					Config: &iop.NodeConfigPayload{
						Runtime: &iop.NodeRuntimeConfig{
							WorkspaceRoot: wsFile.Name(), // file, not dir → storeDSN fails
						},
						Adapters: []*iop.AdapterConfig{
							{
								Type:     "cli",
								Enabled:  true,
								Settings: settings,
							},
						},
					},
				}, nil
			},
		)
		return client
	})
	if err := server.Start(serverCtx); err != nil {
		t.Fatalf("start mock edge server: %v", err)
	}
	t.Cleanup(func() { server.Stop() })

	cfg := &config.NodeConfig{
		Transport: config.TransportConf{
			EdgeAddr: addr,
			Token:    "test-token",
		},
		Logging: config.LoggingConf{Level: "error"},
	}

	app := fx.New(
		bootstrap.Module(cfg),
		fx.NopLogger,
	)

	startCtx, startCancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer startCancel()

	startErr := app.Start(startCtx)
	if startErr == nil {
		_ = app.Stop(context.Background())
		t.Fatal("expected bootstrap to fail when workspace root is an existing file")
	}

	// Allow a brief moment in case a process was spawned despite the fix.
	time.Sleep(150 * time.Millisecond)

	if _, statErr := os.Stat(markerPath); statErr == nil {
		t.Fatal("marker file was created — adapter was started before store was ready")
	}
}
