// -*- Mode: Go; indent-tabs-mode: t -*-
//go:build !darwin

/*
 * Copyright (C) 2026 Canonical Ltd
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package main

import (
	"context"
	"fmt"
	"strings"
	"syscall"

	"github.com/jessevdk/go-flags"

	"github.com/snapcore/snapd/i18n"
	"github.com/snapcore/snapd/snap/squashfs"
	"github.com/snapcore/snapd/strutil"
)

var shortDeltaHelp = i18n.G("Apply/Generate snap delta")
var longDeltaHelp = i18n.G(`
Generate / apply 'smart' delta between source and target snaps

Exactly one of --generate or --apply must be specified.

Formats:
  xdelta3          : raw xdelta3 binary diff
  snap-1-1-xdelta3 : snap-aware xdelta3 delta

Examples:
  $ snap delta --generate --source <source snap file> --target <target snap file> --delta <delta file> --format snap-1-1-xdelta3
  $ snap delta --generate --source <source snap file> --target <target snap file> --delta <delta file> --format xdelta3
  $ snap delta --apply --delta <delta file> --source <source snap file> --target <target snap file>
`)

type cmdDelta struct {
	clientMixin
	Generate bool   `long:"generate"`
	Apply    bool   `long:"apply"`
	Source   string `long:"source" short:"s" required:"yes"`
	Target   string `long:"target" short:"t" required:"yes"`
	Delta    string `long:"delta" short:"d" required:"yes"`
	Format   string `long:"format" short:"f"`
}

// override for testing
var (
	squashfsGenerateDelta = squashfs.GenerateDelta
	squashfsApplyDelta    = squashfs.ApplyDelta
)

func init() {
	cmd := addCommand("delta", shortDeltaHelp, longDeltaHelp, func() flags.Commander { return &cmdDelta{} },
		map[string]string{
			// TRANSLATORS: This should not start with a lowercase letter.
			"generate": i18n.G("Generate delta between source and target"),
			// TRANSLATORS: This should not start with a lowercase letter.
			"apply": i18n.G("Apply delta on the source to reconstruct target"),
			// TRANSLATORS: This should not start with a lowercase letter.
			"source": i18n.G("Source snap package"),
			// TRANSLATORS: This should not start with a lowercase letter.
			"target": i18n.G("Target snap package"),
			// TRANSLATORS: This should not start with a lowercase letter.
			"delta": i18n.G("Delta between source and target snap package"),
			// TRANSLATORS: This should not start with a lowercase letter.
			"format": i18n.G("Delta format algorithm, one of: xdelta3, snap-1-1-xdelta3"),
		}, nil)
	cmd.hidden = true
}

func (x *cmdDelta) Execute(args []string) error {
	// Listen for SIGINT/SIGTERM and cancel the context so that
	// subprocesses (xdelta3, unsquashfs, mksquashfs) are stopped.
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	sigCh, sigStop := signalNotify(syscall.SIGINT, syscall.SIGTERM)
	defer sigStop()
	go func() {
		select {
		case <-sigCh:
			cancel()
		case <-ctx.Done():
		}
	}()

	switch {
	case x.Generate && x.Apply:
		return fmt.Errorf(i18n.G("cannot use --generate and --apply together"))
	case !x.Generate && !x.Apply:
		return fmt.Errorf(i18n.G("one of --generate or --apply must be specified"))
	case x.Generate:
		supportedFormats := squashfs.SupportedDeltaFormats(squashfs.DeltaFormatOpts{WithSnapDeltaFormat: true})
		if x.Format == "" {
			return fmt.Errorf(i18n.G("the --format flag is required for --generate, supported formats: %s"),
				strings.Join(supportedFormats, ", "))
		}
		if !strutil.ListContains(supportedFormats, x.Format) {
			return fmt.Errorf(i18n.G("unsupported delta format %q, supported formats: %s"),
				x.Format, strings.Join(supportedFormats, ", "))
		}
		fmt.Fprintf(Stdout, i18n.G("Using snap delta algorithm '%s'\n"), x.Format)
		fmt.Fprintf(Stdout, i18n.G("Generating delta...\n"))
		return squashfsGenerateDelta(ctx, x.Source, x.Target, x.Delta, x.Format)
	case x.Apply:
		fmt.Fprintf(Stdout, i18n.G("Applying delta...\n"))
		return squashfsApplyDelta(ctx, x.Source, x.Delta, x.Target)
	}

	return nil
}
