package main

import (
	"context"
	"net/http"
	"net/http/httptest"
	"os"
	"strings"
	"testing"
)

// TestIntegrationCRUD menjalankan alur buat-baca-ubah-hapus melawan
// PostgreSQL sungguhan. Tanpa TEST_DATABASE_URL, test ini dilewati
// (skip) supaya `go test ./...` tetap lulus di lingkungan offline:
//
//	TEST_DATABASE_URL=postgres://user:pass@localhost:5432/items?sslmode=disable \
//	  go test -v -run Integration ./...
func TestIntegrationCRUD(t *testing.T) {
	dsn := os.Getenv("TEST_DATABASE_URL")
	if dsn == "" {
		t.Skip("TEST_DATABASE_URL kosong: integration test membutuhkan PostgreSQL aktif")
	}

	ctx := context.Background()
	db, err := OpenDatabase(ctx, dsn)
	if err != nil {
		t.Fatalf("membuka database: %v", err)
	}
	defer db.Close()

	if err := RunMigrations(ctx, db); err != nil {
		t.Fatalf("menjalankan migration: %v", err)
	}

	// Bersihkan data test sebelumnya supaya test dapat diulang.
	if _, err := db.ExecContext(ctx, `TRUNCATE items RESTART IDENTITY`); err != nil {
		t.Fatalf("membersihkan tabel items: %v", err)
	}

	svc := NewItemService(NewPostgresItemRepository(db), RealClock{})
	ts := httptest.NewServer(NewRouter(svc))
	defer ts.Close()

	resp, err := http.Post(ts.URL+"/items", "application/json",
		strings.NewReader(`{"name":"Item Integrasi","price":125000}`))
	if err != nil {
		t.Fatalf("POST /items: %v", err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusCreated {
		t.Fatalf("POST /items status = %d, ingin 201", resp.StatusCode)
	}
	item := bacaEnvelope[Item](t, resp)
	if item.ID != 1 {
		t.Errorf("id = %d, ingin 1 (RESTART IDENTITY)", item.ID)
	}

	resp2, err := http.Get(ts.URL + "/items/1")
	if err != nil {
		t.Fatalf("GET /items/1: %v", err)
	}
	defer resp2.Body.Close()
	if resp2.StatusCode != http.StatusOK {
		t.Fatalf("GET /items/1 status = %d, ingin 200", resp2.StatusCode)
	}
	terbaca := bacaEnvelope[Item](t, resp2)
	if terbaca.Name != "Item Integrasi" || terbaca.Price != 125000 {
		t.Errorf("item terbaca = %+v, tidak sesuai data yang disimpan", terbaca)
	}

	req, err := http.NewRequest(http.MethodDelete, ts.URL+"/items/1", nil)
	if err != nil {
		t.Fatalf("membuat permintaan DELETE: %v", err)
	}
	resp3, err := http.DefaultClient.Do(req)
	if err != nil {
		t.Fatalf("DELETE /items/1: %v", err)
	}
	defer resp3.Body.Close()
	if resp3.StatusCode != http.StatusNoContent {
		t.Fatalf("DELETE /items/1 status = %d, ingin 204", resp3.StatusCode)
	}

	resp4, err := http.Get(ts.URL + "/items/1")
	if err != nil {
		t.Fatalf("GET setelah DELETE: %v", err)
	}
	defer resp4.Body.Close()
	if resp4.StatusCode != http.StatusNotFound {
		t.Fatalf("GET setelah DELETE status = %d, ingin 404", resp4.StatusCode)
	}
}
