package updates

import (
	"context"
	"io"
	"log/slog"
	"time"
)

type Config struct {
	App                AppDescriptor
	Provider           ReleaseProvider
	Downloader         Downloader
	Store              SnapshotStore
	Platform           PlatformInstaller
	Logger             *slog.Logger
	AutoCheckOnStartup bool
}

type AppDescriptor struct {
	ProductID       string   `json:"productID"`
	CurrentVersion  string   `json:"currentVersion"`
	Channel         Channel  `json:"channel"`
	OS              string   `json:"os"`
	Arch            string   `json:"arch"`
	ExecutablePath  string   `json:"executablePath"`
	Args            []string `json:"args,omitempty"`
	WorkingDirectory string  `json:"workingDirectory,omitempty"`
}

type Channel string

const (
	ChannelStable Channel = "stable"
	ChannelBeta   Channel = "beta"
	ChannelAlpha  Channel = "alpha"
)

type State string

const (
	StateIdle            State = "idle"
	StateChecking        State = "checking"
	StateUpToDate        State = "up_to_date"
	StateUpdateAvailable State = "update_available"
	StateDownloading     State = "downloading"
	StateDownloaded      State = "downloaded"
	StateVerifying       State = "verifying"
	StateReadyToApply    State = "ready_to_apply"
	StateApplying        State = "applying"
	StateRestarting      State = "restarting"
	StateFailed          State = "failed"
)

type Snapshot struct {
	State          State          `json:"state"`
	CurrentVersion string         `json:"currentVersion"`
	Channel        Channel        `json:"channel"`
	LastCheckedAt  *time.Time     `json:"lastCheckedAt,omitempty"`
	Candidate      *Release       `json:"candidate,omitempty"`
	Staged         *StagedArtifact `json:"staged,omitempty"`
	LastError      *ErrorInfo     `json:"lastError,omitempty"`
}

type Release struct {
	ID            string    `json:"id"`
	Version       string    `json:"version"`
	Channel       Channel   `json:"channel"`
	NotesMarkdown string    `json:"notesMarkdown,omitempty"`
	PublishedAt   time.Time `json:"publishedAt"`
	Artifact      Artifact  `json:"artifact"`
}

type Artifact struct {
	Kind   ArtifactKind   `json:"kind"`
	Format ArtifactFormat `json:"format"`
	URL    string         `json:"url"`
	SHA256 string         `json:"sha256"`
	Size   int64          `json:"size,omitempty"`
}

type ArtifactKind string

const ArtifactKindBundleArchive ArtifactKind = "bundle-archive"

type ArtifactFormat string

const (
	ArtifactFormatZip   ArtifactFormat = "zip"
	ArtifactFormatTarGz ArtifactFormat = "tar.gz"
)

type ErrorInfo struct {
	Code    string `json:"code"`
	Message string `json:"message"`
}

type ResolveRequest struct {
	App AppDescriptor
}

type CheckRequest struct{}

type DownloadedFile struct {
	Path   string
	Size   int64
	SHA256 string
}

type InstallRoot struct {
	Path string `json:"path"`
}

type BundleManifest struct {
	SchemaVersion int          `json:"schemaVersion"`
	EntryPoint    string       `json:"entrypoint"`
	Files         []BundleFile `json:"files"`
}

type BundleFile struct {
	Path string `json:"path"`
	Mode string `json:"mode"`
}

type StagedArtifact struct {
	Path    string         `json:"path"`
	Root    InstallRoot    `json:"root"`
	Release Release        `json:"release"`
	Bundle  BundleManifest `json:"bundle"`
}

type ApplyRequest struct {
	App    AppDescriptor
	Root   InstallRoot
	Staged StagedArtifact
}

type ReleaseProvider interface {
	Resolve(ctx context.Context, req ResolveRequest) (*Release, error)
	OpenArtifact(ctx context.Context, rel Release) (io.ReadCloser, error)
}

type SnapshotStore interface {
	Load(ctx context.Context) (Snapshot, error)
	Save(ctx context.Context, snapshot Snapshot) error
	ClearStaged(ctx context.Context) error
}

type Downloader interface {
	Download(ctx context.Context, artifact Artifact) (DownloadedFile, error)
}

type PlatformInstaller interface {
	DetectInstallRoot(app AppDescriptor) (InstallRoot, error)
	Stage(ctx context.Context, root InstallRoot, file DownloadedFile, release Release) (StagedArtifact, error)
	SpawnApplyAndRestart(ctx context.Context, req ApplyRequest) error
}
