package restic

import (
	"testing"
	"time"
)

// Embed test JSON as string constants so tests are self-contained and
// don't depend on external files.

const snapshotFixtureNormal = `[
  {
    "time": "2026-03-15T10:30:00.123456789+01:00",
    "parent": "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789",
    "tree": "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
    "paths": ["/home/user"],
    "hostname": "myhost",
    "username": "user",
    "uid": 1000,
    "gid": 1000,
    "excludes": ["*.tmp"],
    "tags": ["daily", "important"],
    "program_version": "restic 0.18.1",
    "id": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
    "short_id": "a1b2c3d4"
  },
  {
    "time": "2026-03-14T08:00:00Z",
    "tree": "fedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321",
    "paths": ["/home/user", "/etc"],
    "hostname": "server",
    "username": "root",
    "uid": 0,
    "gid": 0,
    "program_version": "restic 0.18.1",
    "id": "b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3",
    "short_id": "b2c3d4e5"
  }
]`

const snapshotFixtureEmpty = `[]`

const snapshotFixtureSingleNoTags = `[
  {
    "time": "2026-01-01T00:00:00Z",
    "tree": "0000000000000000000000000000000000000000000000000000000000000000",
    "paths": ["/srv/data"],
    "hostname": "backup-host",
    "username": "backup",
    "uid": 1001,
    "gid": 1001,
    "program_version": "restic 0.17.0",
    "id": "c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4",
    "short_id": "c3d4e5f6"
  }
]`

const snapshotFixtureManyPaths = `[
  {
    "time": "2026-06-15T14:22:33Z",
    "tree": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
    "paths": ["/home/user/Documents", "/home/user/Music", "/home/user/Pictures", "/home/user/Videos"],
    "hostname": "workstation",
    "username": "user",
    "uid": 1000,
    "gid": 1000,
    "tags": ["weekly"],
    "program_version": "restic 0.18.1",
    "id": "d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5",
    "short_id": "d4e5f6a7"
  }
]`

func TestParseSnapshots(t *testing.T) {
	t.Parallel()

	tests := []struct {
		name      string
		json      string
		wantCount int
		wantErr   bool
	}{
		{
			name:      "normal with two snapshots",
			json:      snapshotFixtureNormal,
			wantCount: 2,
			wantErr:   false,
		},
		{
			name:      "empty array",
			json:      snapshotFixtureEmpty,
			wantCount: 0,
			wantErr:   false,
		},
		{
			name:      "single snapshot without tags",
			json:      snapshotFixtureSingleNoTags,
			wantCount: 1,
			wantErr:   false,
		},
		{
			name:      "snapshot with many paths",
			json:      snapshotFixtureManyPaths,
			wantCount: 1,
			wantErr:   false,
		},
		{
			name:      "invalid JSON",
			json:      `{not valid json`,
			wantCount: 0,
			wantErr:   true,
		},
		{
			name:      "null",
			json:      `null`,
			wantCount: 0,
			wantErr:   false,
		},
	}

	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()

			snapshots, err := ParseSnapshots([]byte(tt.json))
			if (err != nil) != tt.wantErr {
				t.Fatalf("ParseSnapshots(): err=%v, wantErr=%v", err, tt.wantErr)
			}
			if err != nil {
				return
			}
			if len(snapshots) != tt.wantCount {
				t.Fatalf("ParseSnapshots(): got %d snapshots, want %d", len(snapshots), tt.wantCount)
			}
		})
	}
}

func TestParseSnapshotsFields(t *testing.T) {
	t.Parallel()

	snapshots, err := ParseSnapshots([]byte(snapshotFixtureNormal))
	if err != nil {
		t.Fatalf("ParseSnapshots(): unexpected error: %v", err)
	}
	if len(snapshots) != 2 {
		t.Fatalf("expected 2 snapshots, got %d", len(snapshots))
	}

	// First snapshot: has tags, single path, has parent.
	s := snapshots[0]
	if s.ID != "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2" {
		t.Errorf("ID = %q", s.ID)
	}
	if s.ShortID != "a1b2c3d4" {
		t.Errorf("ShortID = %q", s.ShortID)
	}
	if s.Hostname != "myhost" {
		t.Errorf("Hostname = %q", s.Hostname)
	}
	if len(s.Paths) != 1 || s.Paths[0] != "/home/user" {
		t.Errorf("Paths = %v", s.Paths)
	}
	if len(s.Tags) != 2 || s.Tags[0] != "daily" || s.Tags[1] != "important" {
		t.Errorf("Tags = %v", s.Tags)
	}
	expectedTime := time.Date(2026, 3, 15, 10, 30, 0, 123456789, time.FixedZone("", 3600))
	if !s.Time.Equal(expectedTime) {
		t.Errorf("Time = %v, want %v", s.Time, expectedTime)
	}

	// Second snapshot: no tags, no parent, multiple paths.
	s2 := snapshots[1]
	if s2.ShortID != "b2c3d4e5" {
		t.Errorf("ShortID = %q", s2.ShortID)
	}
	if s2.Hostname != "server" {
		t.Errorf("Hostname = %q", s2.Hostname)
	}
	if len(s2.Paths) != 2 {
		t.Errorf("Paths = %v", s2.Paths)
	}
	if len(s2.Tags) != 0 {
		t.Errorf("Tags = %v, want empty", s2.Tags)
	}
}

func TestFormatSnapshotLine(t *testing.T) {
	t.Parallel()

	tests := []struct {
		name     string
		snapshot Snapshot
		want     string
	}{
		{
			name: "full snapshot with tags",
			snapshot: Snapshot{
				ShortID:  "a1b2c3d4",
				Time:     time.Date(2026, 3, 15, 10, 30, 0, 0, time.UTC),
				Hostname: "myhost",
				Paths:    []string{"/home/user"},
				Tags:     []string{"daily", "important"},
			},
			want: "a1b2c3d4  2026-03-15 10:30  myhost  /home/user  [daily, important]",
		},
		{
			name: "no tags",
			snapshot: Snapshot{
				ShortID:  "b2c3d4e5",
				Time:     time.Date(2026, 3, 14, 8, 0, 0, 0, time.UTC),
				Hostname: "server",
				Paths:    []string{"/etc"},
			},
			want: "b2c3d4e5  2026-03-14 08:00  server  /etc",
		},
		{
			name: "multiple paths",
			snapshot: Snapshot{
				ShortID:  "c3d4e5f6",
				Time:     time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC),
				Hostname: "backup-host",
				Paths:    []string{"/home", "/etc", "/srv"},
				Tags:     []string{"full"},
			},
			want: "c3d4e5f6  2026-01-01 00:00  backup-host  /home, /etc, /srv  [full]",
		},
		{
			name: "empty paths",
			snapshot: Snapshot{
				ShortID:  "d4e5f6a7",
				Time:     time.Date(2026, 6, 15, 14, 22, 0, 0, time.UTC),
				Hostname: "box",
			},
			want: "d4e5f6a7  2026-06-15 14:22  box",
		},
		{
			name: "empty hostname",
			snapshot: Snapshot{
				ShortID: "e5f6a7b8",
				Time:    time.Date(2026, 2, 28, 12, 0, 0, 0, time.UTC),
				Paths:   []string{"/data"},
			},
			want: "e5f6a7b8  2026-02-28 12:00  (unknown)  /data",
		},
	}

	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()

			got := FormatSnapshotLine(tt.snapshot)
			if got != tt.want {
				t.Errorf("FormatSnapshotLine():\n  got  %q\n  want %q", got, tt.want)
			}
		})
	}
}
