package model

import (
	"strings"
	"testing"
)

func TestParseSize(t *testing.T) {
	const (
		kib = int64(1024)
		mib = int64(1024 * 1024)
		gib = int64(1024 * 1024 * 1024)
	)

	cases := []struct {
		name       string
		input      string
		want       int64
		wantErrSub string
	}{
		// Happy path — short suffixes.
		{"1G", "1G", gib, ""},
		{"512M", "512M", 512 * mib, ""},
		{"4K", "4K", 4 * kib, ""},
		{"4G", "4G", 4 * gib, ""},

		// GiB/MiB/KiB suffixes — parser now accepts these.
		{"4GiB", "4GiB", 4 * gib, ""},
		{"512MiB", "512MiB", 512 * mib, ""},
		{"4KiB", "4KiB", 4 * kib, ""},

		// Lowercase — ToUpper normalises; should work like uppercase.
		{"lowercase 1g", "1g", gib, ""},
		{"lowercase 512m", "512m", 512 * mib, ""},
		{"lowercase 4gib", "4gib", 4 * gib, ""},

		// No-suffix — treated as MiB (the parser's default multiplier is 1 MiB).
		// "1024" → 1024 MiB, "1" → 1 MiB.
		{"no-suffix 1024", "1024", 1024 * mib, ""},
		{"no-suffix 1", "1", mib, ""},

		// Whitespace trimming.
		{"leading space", " 2G", 2 * gib, ""},
		{"trailing space", "2G ", 2 * gib, ""},
		{"both spaces", " 2G ", 2 * gib, ""},

		// Error cases.
		{"empty string", "", 0, "required"},
		{"whitespace only", "   ", 0, "required"},
		{"unknown suffix B", "512B", 0, "unsupported size suffix"},
		{"negative", "-1G", 0, "positive"},
		{"zero", "0G", 0, "positive"},
		{"overflow MaxDiskBytes", "129G", 0, "exceeds max"},
		{"non-numeric", "xG", 0, "parse size"},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			got, err := ParseSize(tc.input)
			if tc.wantErrSub != "" {
				if err == nil {
					t.Fatalf("ParseSize(%q) = %d, want error containing %q", tc.input, got, tc.wantErrSub)
				}
				if !strings.Contains(err.Error(), tc.wantErrSub) {
					t.Fatalf("ParseSize(%q) error = %q, want substring %q", tc.input, err.Error(), tc.wantErrSub)
				}
				return
			}
			if err != nil {
				t.Fatalf("ParseSize(%q) unexpected error: %v", tc.input, err)
			}
			if got != tc.want {
				t.Fatalf("ParseSize(%q) = %d, want %d", tc.input, got, tc.want)
			}
		})
	}
}

func TestFormatSizeBytes(t *testing.T) {
	const (
		kib = int64(1024)
		mib = int64(1024 * 1024)
		gib = int64(1024 * 1024 * 1024)
	)

	cases := []struct {
		name  string
		input int64
		want  string
	}{
		// FormatSizeBytes(0): 0 is divisible by GiB so it formats as "0G".
		{"0", 0, "0G"},
		{"1 byte", 1, "1"},
		{"1 KiB", kib, "1K"},
		{"4 KiB", 4 * kib, "4K"},
		{"1 MiB", mib, "1M"},
		{"512 MiB", 512 * mib, "512M"},
		{"1 GiB", gib, "1G"},
		{"4 GiB", 4 * gib, "4G"},
		{"128 GiB (max disk)", 128 * gib, "128G"},
		// Non-round: falls through to raw bytes.
		{"non-round bytes", 1500, "1500"},
		{"non-round MiB", 3*mib + 1, "3145729"},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			got := FormatSizeBytes(tc.input)
			if got != tc.want {
				t.Fatalf("FormatSizeBytes(%d) = %q, want %q", tc.input, got, tc.want)
			}
		})
	}
}

func TestParseSizeFormatRoundTrip(t *testing.T) {
	const (
		kib = int64(1024)
		mib = int64(1024 * 1024)
		gib = int64(1024 * 1024 * 1024)
	)

	boundaries := []int64{kib, 4 * kib, mib, 512 * mib, gib, 4 * gib, 8 * gib}
	for _, n := range boundaries {
		formatted := FormatSizeBytes(n)
		parsed, err := ParseSize(formatted)
		if err != nil {
			t.Errorf("ParseSize(FormatSizeBytes(%d) = %q): %v", n, formatted, err)
			continue
		}
		if parsed != n {
			t.Errorf("round-trip(%d): FormatSizeBytes → %q → ParseSize → %d", n, formatted, parsed)
		}
	}
}
