package sysupdate

import (
	"context"
	"os/exec"
	"strings"
)

func init() {
	RegisterOverlayBackend(func() Backend { return &flatpakBackend{} })
}

type flatpakBackend struct{}

func (flatpakBackend) ID() string                         { return "flatpak" }
func (flatpakBackend) DisplayName() string                { return "Flatpak" }
func (flatpakBackend) Repo() RepoKind                     { return RepoFlatpak }
func (flatpakBackend) NeedsAuth() bool                    { return false }
func (flatpakBackend) RunsInTerminal() bool               { return false }
func (flatpakBackend) IsAvailable(_ context.Context) bool { return commandExists("flatpak") }

func (flatpakBackend) CheckUpdates(ctx context.Context) ([]Package, error) {
	cmd := exec.CommandContext(ctx, "flatpak", "remote-ls", "--updates", "--columns=application,version,branch,commit,name")
	out, err := cmd.Output()
	if err != nil {
		return nil, err
	}
	installed := flatpakInstalled(ctx)
	return parseFlatpakUpdates(string(out), installed), nil
}

func flatpakInstalled(ctx context.Context) map[string]flatpakInstalledEntry {
	out, err := exec.CommandContext(ctx, "flatpak", "list", "--columns=application,version,branch,active").Output()
	if err != nil {
		return nil
	}
	m := make(map[string]flatpakInstalledEntry)
	for line := range strings.SplitSeq(string(out), "\n") {
		if line == "" {
			continue
		}
		fields := strings.Split(line, "\t")
		if len(fields) == 0 || fields[0] == "" {
			continue
		}
		appID := fields[0]
		entry := flatpakInstalledEntry{}
		if len(fields) > 1 {
			entry.version = fields[1]
		}
		if len(fields) > 2 {
			entry.branch = fields[2]
		}
		if len(fields) > 3 {
			entry.commit = fields[3]
		}
		key := appID
		if entry.branch != "" {
			key = appID + "//" + entry.branch
		}
		m[key] = entry
	}
	return m
}

type flatpakInstalledEntry struct {
	version string
	branch  string
	commit  string
}

func (flatpakBackend) Upgrade(ctx context.Context, opts UpgradeOptions, onLine func(string)) error {
	argv := []string{"flatpak", "update", "-y", "--noninteractive"}
	if opts.DryRun {
		argv = []string{"flatpak", "update", "--no-deploy", "-y"}
	}
	return Run(ctx, argv, RunOptions{OnLine: onLine})
}

func parseFlatpakUpdates(text string, installed map[string]flatpakInstalledEntry) []Package {
	if text == "" {
		return nil
	}
	var pkgs []Package
	for line := range strings.SplitSeq(text, "\n") {
		if line == "" {
			continue
		}
		fields := strings.Split(line, "\t")
		if len(fields) == 0 || fields[0] == "" {
			continue
		}
		appID := fields[0]
		version, branch, commit := "", "", ""
		if len(fields) > 1 {
			version = fields[1]
		}
		if len(fields) > 2 {
			branch = fields[2]
		}
		if len(fields) > 3 {
			commit = fields[3]
		}
		display := appID
		if len(fields) > 4 && fields[4] != "" {
			display = fields[4]
		}

		key := appID
		if branch != "" {
			key = appID + "//" + branch
		}
		inst := installed[key]
		from, to := flatpakVersionPair(inst.version, inst.commit, version, commit)

		pkgs = append(pkgs, Package{
			Name:        display,
			Repo:        RepoFlatpak,
			Backend:     "flatpak",
			FromVersion: from,
			ToVersion:   to,
		})
	}
	return pkgs
}

func flatpakVersionPair(installedVer, installedCommit, remoteVer, remoteCommit string) (from, to string) {
	if remoteVer != "" {
		return installedVer, remoteVer
	}
	return shortCommit(installedCommit), shortCommit(remoteCommit)
}

func shortCommit(c string) string {
	if len(c) > 8 {
		return c[:8]
	}
	return c
}
