package main

import (
	"errors"
	"fmt"
)

// ErrRekamanTidakAda adalah error domain milik lapisan repository.
var ErrRekamanTidakAda = errors.New("rekaman tidak ditemukan")

// ErrorSQL adalah error internal lapisan repository.
type ErrorSQL struct {
	Query string
	Sebab error
}

func (e *ErrorSQL) Error() string {
	return fmt.Sprintf("query %q gagal: %v", e.Query, e.Sebab)
}

func (e *ErrorSQL) Unwrap() error {
	return e.Sebab
}

// ErrorAPI adalah error publik yang aman dikirim ke klien.
type ErrorAPI struct {
	Kode  int
	Pesan string
}

func (e *ErrorAPI) Error() string {
	return fmt.Sprintf("status %d: %s", e.Kode, e.Pesan)
}

func repoAmbilPengguna(id string) (string, error) {
	switch id {
	case "p-404":
		return "", fmt.Errorf("repo pengguna: %w", ErrRekamanTidakAda)
	case "p-sql":
		return "", &ErrorSQL{Query: "SELECT * FROM pengguna", Sebab: errors.New("driver: koneksi direset")}
	}
	return "Budi Santoso", nil
}

func serviceAmbilPengguna(id string) (string, error) {
	nama, err := repoAmbilPengguna(id)
	if err != nil {
		return "", fmt.Errorf("service pengguna: %w", err)
	}
	return nama, nil
}

func handlerAmbilPengguna(id string) *ErrorAPI {
	nama, err := serviceAmbilPengguna(id)
	if err != nil {
		if errors.Is(err, ErrRekamanTidakAda) {
			return &ErrorAPI{Kode: 404, Pesan: "pengguna tidak ditemukan"}
		}
		fmt.Println("log internal:", err)
		return &ErrorAPI{Kode: 500, Pesan: "kesalahan server internal"}
	}
	fmt.Printf("200 OK: nama=%q\n", nama)
	return nil
}

func main() {
	for _, id := range []string{"p-1", "p-404", "p-sql"} {
		if apiErr := handlerAmbilPengguna(id); apiErr != nil {
			fmt.Printf("respons API: %v\n", apiErr)
		}
	}
}
