package main

import (
	"bytes"
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"
)

// penggunaUji menjalankan Run dengan penyimpanan baru berisi t.TempDir(),
// waktu beku, dan penampung keluaran. Hasilnya stdout, stderr, dan exit code.
func penggunaUji(t *testing.T, args ...string) (string, string, int) {
	t.Helper()
	p := PenyimpananBaru(filepath.Join(t.TempDir(), "tugas.json"))
	var out, errOut bytes.Buffer
	exit := Run(args, p, func() time.Time { return waktuBeku }, &out, &errOut)
	return out.String(), errOut.String(), exit
}

func TestRunTanpaSubperintah(t *testing.T) {
	stdout, stderr, exit := penggunaUji(t)

	if exit != exitPenggunaan {
		t.Errorf("exit = %d; want %d", exit, exitPenggunaan)
	}
	if stdout != "" {
		t.Errorf("stdout = %q; want kosong", stdout)
	}
	if !strings.Contains(stderr, "Penggunaan:") {
		t.Errorf("stderr = %q; want pesan penggunaan", stderr)
	}
}

func TestRunAlurLengkap(t *testing.T) {
	p := PenyimpananBaru(filepath.Join(t.TempDir(), "tugas.json"))
	sekarang := func() time.Time { return waktuBeku }

	jalankan := func(wantExit int, args ...string) string {
		t.Helper()
		var out, errOut bytes.Buffer
		if got := Run(args, p, sekarang, &out, &errOut); got != wantExit {
			t.Fatalf("Run(%v) exit = %d; want %d", args, got, wantExit)
		}
		if errOut.Len() > 0 {
			t.Fatalf("Run(%v) stderr tak terduga: %q", args, errOut.String())
		}
		return out.String()
	}

	stdout := jalankan(exitSukses, "tambah", "Belajar Go")
	if stdout != "Tugas 1 \"Belajar Go\" tersimpan.\n" {
		t.Errorf("tambah stdout = %q", stdout)
	}
	jalankan(exitSukses, "tambah", "Tulis dokumentasi")
	jalankan(exitSukses, "selesai", "2")

	stdout = jalankan(exitSukses, "list")
	want := "ID  STATUS  JUDUL\n1   [ ]     Belajar Go\n"
	if stdout != want {
		t.Errorf("list stdout = %q; want %q", stdout, want)
	}

	stdout = jalankan(exitSukses, "list", "--semua")
	want = "ID  STATUS  JUDUL\n1   [ ]     Belajar Go\n2   [x]     Tulis dokumentasi\n"
	if stdout != want {
		t.Errorf("list --semua stdout = %q; want %q", stdout, want)
	}

	jalankan(exitSukses, "hapus", "1")
	stdout = jalankan(exitSukses, "list")
	if stdout != "Tidak ada tugas yang belum selesai.\n" {
		t.Errorf("list setelah hapus stdout = %q", stdout)
	}
}

func TestRunTulisJSON(t *testing.T) {
	berkas := filepath.Join(t.TempDir(), "tugas.json")
	p := PenyimpananBaru(berkas)
	var out, errOut bytes.Buffer

	if got := Run([]string{"tambah", "Belajar Go"}, p, func() time.Time { return waktuBeku }, &out, &errOut); got != exitSukses {
		t.Fatalf("Run tambah exit = %d; want %d", got, exitSukses)
	}

	data, err := os.ReadFile(berkas)
	if err != nil {
		t.Fatalf("membaca berkas hasil tambah: %v", err)
	}
	want := `[
  {
    "id": 1,
    "judul": "Belajar Go",
    "selesai": false,
    "dibuat": "2026-01-15T08:00:00Z"
  }
]
`
	if string(data) != want {
		t.Errorf("isi berkas = %q; want %q", data, want)
	}
}

func TestRunKesalahan(t *testing.T) {
	cases := []struct {
		nama             string
		args             []string
		wantExit         int
		wantErrSepenggal string
	}{
		{"subperintah tak dikenal", []string{"lihat"}, exitPenggunaan, "tidak dikenal"},
		{"tambah tanpa judul", []string{"tambah"}, exitPenggunaan, "membutuhkan satu judul"},
		{"tambah dua judul", []string{"tambah", "a", "b"}, exitPenggunaan, "membutuhkan satu judul"},
		{"tambah judul kosong", []string{"tambah", "   "}, exitPenggunaan, "judul tugas tidak boleh kosong"},
		{"selesai id bukan angka", []string{"selesai", "abc"}, exitPenggunaan, "harus berupa angka"},
		{"selesai id negatif", []string{"selesai", "-1"}, exitPenggunaan, "harus berupa angka"},
		{"selesai id tidak ada", []string{"selesai", "9"}, exitData, "tidak ditemukan"},
		{"hapus id tidak ada", []string{"hapus", "9"}, exitData, "tidak ditemukan"},
		{"list flag tak dikenal", []string{"list", "--salah"}, exitPenggunaan, "flag provided but not defined"},
	}

	for _, c := range cases {
		t.Run(c.nama, func(t *testing.T) {
			stdout, stderr, exit := penggunaUji(t, c.args...)

			if exit != c.wantExit {
				t.Errorf("exit = %d; want %d", exit, c.wantExit)
			}
			if stdout != "" {
				t.Errorf("stdout = %q; want kosong", stdout)
			}
			if !strings.Contains(stderr, c.wantErrSepenggal) {
				t.Errorf("stderr = %q; want pesan memuat %q", stderr, c.wantErrSepenggal)
			}
		})
	}
}

func TestRunListKosong(t *testing.T) {
	stdout, _, exit := penggunaUji(t, "list")

	if exit != exitSukses {
		t.Errorf("exit = %d; want %d", exit, exitSukses)
	}
	if stdout != "Belum ada tugas tersimpan.\n" {
		t.Errorf("list pada penyimpanan kosong stdout = %q", stdout)
	}
}
