package wails

import (
	"context"
	"io"
	"path/filepath"
	"testing"
	"time"

	"github.com/Eriyc/rules_wails/pkg/wails3kit/updates"
	filestore "github.com/Eriyc/rules_wails/pkg/wails3kit/updates/storage/file"
	"github.com/wailsapp/wails/v3/pkg/application"
)

func TestServiceEmitsSnapshots(t *testing.T) {
	t.Parallel()

	release := &updates.Release{
		Version: "1.1.0",
		Channel: updates.ChannelStable,
		Artifact: updates.Artifact{
			Kind:   updates.ArtifactKindBundleArchive,
			Format: updates.ArtifactFormatZip,
			URL:    "https://example.invalid/app.zip",
			SHA256: "abc",
		},
	}
	controller, err := updates.NewController(updates.Config{
		App: updates.AppDescriptor{
			ProductID:      "com.example.app",
			CurrentVersion: "1.0.0",
			Channel:        updates.ChannelStable,
			OS:             "linux",
			Arch:           "amd64",
			ExecutablePath: filepath.Join(t.TempDir(), "App"),
		},
		Provider:   serviceFakeProvider{release: release},
		Downloader: serviceFakeDownloader{},
		Store:      filestore.New(filepath.Join(t.TempDir(), "snapshot.json")),
		Platform:   &serviceFakePlatform{root: updates.InstallRoot{Path: t.TempDir()}},
	})
	if err != nil {
		t.Fatalf("NewController returned error: %v", err)
	}

	emitted := make(chan updates.Snapshot, 2)
	service := NewService(Options{
		Controller: controller,
		Emitter: func(_ string, data any) {
			emitted <- data.(updates.Snapshot)
		},
	})
	if err := service.ServiceStartup(context.Background(), application.ServiceOptions{}); err != nil {
		t.Fatalf("ServiceStartup returned error: %v", err)
	}
	defer service.ServiceShutdown()

	if _, err := service.Check(); err != nil {
		t.Fatalf("Check returned error: %v", err)
	}

	select {
	case snapshot := <-emitted:
		if snapshot.State == "" {
			t.Fatal("expected emitted snapshot state")
		}
	case <-time.After(time.Second):
		t.Fatal("timed out waiting for emitted snapshot")
	}
}

type serviceFakeProvider struct {
	release *updates.Release
}

func (provider serviceFakeProvider) Resolve(context.Context, updates.ResolveRequest) (*updates.Release, error) {
	return provider.release, nil
}

func (provider serviceFakeProvider) OpenArtifact(context.Context, updates.Release) (io.ReadCloser, error) {
	return nil, nil
}

type serviceFakeDownloader struct{}

func (serviceFakeDownloader) Download(context.Context, updates.Artifact) (updates.DownloadedFile, error) {
	return updates.DownloadedFile{}, nil
}

type serviceFakePlatform struct {
	root updates.InstallRoot
}

func (platform *serviceFakePlatform) DetectInstallRoot(updates.AppDescriptor) (updates.InstallRoot, error) {
	return platform.root, nil
}

func (platform *serviceFakePlatform) Stage(context.Context, updates.InstallRoot, updates.DownloadedFile, updates.Release) (updates.StagedArtifact, error) {
	return updates.StagedArtifact{}, nil
}

func (platform *serviceFakePlatform) SpawnApplyAndRestart(context.Context, updates.ApplyRequest) error {
	return nil
}
