package appexport

import (
	"errors"
	"io"
	"log"
	"strings"
	"testing"

	"gitea.dooplex.hu/admin/felhom-controller/internal/appbackup"
)

// hddProvider is an rtProvider that reports an HDD-backed stack (estimate scenario H + the
// v0.130.0 additive-export tests in export_additive_test.go).
type hddProvider struct {
	*rtProvider
	mounts   []string
	hddPath  string
	binds    []appbackup.ClassifiedBind
	hasBinds bool
}

func (p *hddProvider) GetStackNeedsHDD(string) bool      { return true }
func (p *hddProvider) GetStackHDDMounts(string) []string { return p.mounts }
func (p *hddProvider) GetStackHDDPath(string) string     { return p.hddPath }
func (p *hddProvider) GetStackClassifiedBinds(string) ([]appbackup.ClassifiedBind, bool) {
	return p.binds, p.hasBinds
}

func newEstimator(t *testing.T, provider ExportStackProvider) *Exporter {
	t.Helper()
	return NewExporter(provider, log.New(io.Discard, "", 0), "test")
}

// Scenario F (the F-A fix): a volume-only app with a >1 GiB volume reports the REAL size via the
// container-view sizer — not 0/"3.6 KB". This is the F-A red-proof anchor (revert EstimateExport to
// dockerVolumeSize → reads 0).
func TestEstimate_VolumeSize_RealNotZero(t *testing.T) {
	const twoGiB = int64(2) << 30
	orig := volumeSizer
	volumeSizer = func(vol string) (int64, error) { return twoGiB, nil }
	defer func() { volumeSizer = orig }()

	e := newEstimator(t, &rtProvider{stackDir: t.TempDir(), volumes: []string{"app_data"}})
	est, err := e.EstimateExport("app", t.TempDir())
	if err != nil {
		t.Fatal(err)
	}
	if est.SizeUnknown {
		t.Fatalf("size must be known when the sizer succeeds")
	}
	if est.DataSizeBytes != twoGiB {
		t.Fatalf("DataSizeBytes = %d, want %d (WRONG would be 0 — the F-A bug)", est.DataSizeBytes, twoGiB)
	}
	if !strings.Contains(est.DataSizeHuman, "GB") {
		t.Fatalf("DataSizeHuman = %q, want GB-scale (WRONG would be \"3.6 KB\")", est.DataSizeHuman)
	}
}

// Scenario G: a failed volume read must never render as "fits". Size is marked unknown, the human
// string says so, and FitsOnDest is forced false.
func TestEstimate_VolumeSize_FailureNeverFits(t *testing.T) {
	orig := volumeSizer
	volumeSizer = func(vol string) (int64, error) { return 0, errors.New("docker: no such image") }
	defer func() { volumeSizer = orig }()

	e := newEstimator(t, &rtProvider{stackDir: t.TempDir(), volumes: []string{"app_data"}})
	est, err := e.EstimateExport("app", t.TempDir())
	if err != nil {
		t.Fatal(err)
	}
	if !est.SizeUnknown {
		t.Fatalf("a failed volume read must set SizeUnknown")
	}
	if est.FitsOnDest {
		t.Fatalf("an unknown size must NEVER render as fits_on_dest:true")
	}
	if est.DataSizeHuman != "ismeretlen méret" {
		t.Fatalf("DataSizeHuman = %q, want \"ismeretlen méret\"", est.DataSizeHuman)
	}
	if est.DataSizeBytes != 0 {
		t.Fatalf("no successful read → DataSizeBytes should be 0, got %d", est.DataSizeBytes)
	}
}

// Scenario H (regression): an HDD-backed stack must NOT touch the new volume sizer — the HDD branch
// (duBytes on the mounted /mnt path) is unchanged. Platform-independent: assert the seam is not
// invoked and SizeUnknown stays false.
func TestEstimate_HDDPath_DoesNotUseVolumeSizer(t *testing.T) {
	called := false
	orig := volumeSizer
	volumeSizer = func(vol string) (int64, error) { called = true; return 0, nil }
	defer func() { volumeSizer = orig }()

	p := &hddProvider{rtProvider: &rtProvider{stackDir: t.TempDir()}, mounts: []string{t.TempDir()}}
	e := newEstimator(t, p)
	est, err := e.EstimateExport("app", t.TempDir())
	if err != nil {
		t.Fatal(err)
	}
	if called {
		t.Fatalf("HDD-backed stack must not call the docker volume sizer")
	}
	if est.SizeUnknown {
		t.Fatalf("HDD branch must not set SizeUnknown")
	}
}
