package salestargets

import (
	"context"
	"encoding/json"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/TaruLab/keluargamart-backend/internal/auth"
)

// gmUserID is the PIC account linked to targets in these tests.
const gmUserID = int64(7)

func withUser(u *auth.User) context.Context {
	return auth.ToContext(context.Background(), u, 1)
}

// gateRepo lets a request that passes the write gate return a valid target
// without touching a database — the tests exercise the gate, not the repo.
type gateRepo struct{}

func (g *gateRepo) Create(ctx context.Context, in TargetInput, createdBy int64) (*SalesTarget, error) {
	return &SalesTarget{ID: 1, Period: in.Period, RoleLevel: in.RoleLevel, UserID: in.UserID, TargetAmount: in.TargetAmount, CreatedBy: &createdBy}, nil
}

func (g *gateRepo) List(context.Context, string, string, string, int64) ([]SalesTarget, error) {
	return []SalesTarget{}, nil
}
func (g *gateRepo) Summary(context.Context, string) ([]LevelSummary, error) { return nil, nil }
func (g *gateRepo) Update(context.Context, int64, TargetInput) (*SalesTarget, error) {
	return &SalesTarget{}, nil
}
func (g *gateRepo) Delete(context.Context, int64) error { return nil }

func newGateHandler() *Handler {
	return NewHandler(&gateRepo{})
}

func post(r *Handler, body string, u *auth.User) *httptest.ResponseRecorder {
	req := httptest.NewRequest("POST", "/api/v1/sales-targets", strings.NewReader(body))
	req = req.WithContext(withUser(u))
	w := httptest.NewRecorder()
	r.Create(w, req)
	return w
}

func gm() *auth.User    { return &auth.User{ID: 1, Role: auth.RoleGM} }
func bm() *auth.User    { return &auth.User{ID: 2, Role: auth.RoleBM} }
func spv() *auth.User   { return &auth.User{ID: 3, Role: auth.RoleSPV} }
func sales() *auth.User { return &auth.User{ID: 4, Role: auth.RoleSales} }
func admin() *auth.User { return &auth.User{ID: 5, Role: auth.RoleAdmin} }

func TestCreate_GMOnly(t *testing.T) {
	h := newGateHandler()

	valid := func(role string) string {
		scope := map[string]string{"gm": "kota", "bm": "kecamatan", "spv": "kelurahan", "sales": "desa"}[role]
		return `{"period":"2026-09","role_level":"` + role + `","scope_level":"` + scope + `","scope_name":"X","user_id":` + itoa(gmUserID) + `,"target_amount":1000000}`
	}

	if w := post(h, valid("gm"), gm()); w.Code != 201 {
		t.Errorf("GM create = %d, want 201 (repo nil panics only after the gate)", w.Code)
	}
	if w := post(h, valid("sales"), bm()); w.Code != 403 {
		t.Errorf("BM create = %d, want 403 (GM-only policy)", w.Code)
	}
	if w := post(h, valid("sales"), spv()); w.Code != 403 {
		t.Errorf("SPV create = %d, want 403", w.Code)
	}
	if w := post(h, valid("sales"), sales()); w.Code != 403 {
		t.Errorf("sales create = %d, want 403", w.Code)
	}
	if w := post(h, valid("gm"), admin()); w.Code != 201 {
		t.Errorf("admin create = %d, want 201", w.Code)
	}
}

func TestCreate_RequiresLinkedAccount(t *testing.T) {
	h := newGateHandler()
	body := `{"period":"2026-09","role_level":"gm","scope_level":"kota","scope_name":"Kota Binjai","target_amount":5000000}`
	w := post(h, body, gm())
	if w.Code != 400 {
		t.Fatalf("create without user_id = %d, want 400", w.Code)
	}
	var out struct {
		Error struct{ Message string }
	}
	_ = json.Unmarshal(w.Body.Bytes(), &out)
	if !strings.Contains(out.Error.Message, "PIC") {
		t.Errorf("message = %q, want it to demand a linked PIC account", out.Error.Message)
	}
}

func TestCanManageTargets(t *testing.T) {
	for _, u := range []*auth.User{gm(), admin(), &auth.User{ID: 6, Role: auth.RoleOwner}, &auth.User{ID: 6, Role: auth.RoleSystem}} {
		if !canManageTargets(u) {
			t.Errorf("role %s should manage targets", u.Role)
		}
	}
	for _, u := range []*auth.User{bm(), spv(), sales(), nil} {
		if canManageTargets(u) {
			t.Errorf("role %v should NOT manage targets", u)
		}
	}
}

func itoa(n int64) string {
	b, _ := json.Marshal(n)
	return string(b)
}
