package sales

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

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

// statusGuardSetup builds a handler with one pending_approval order owned by
// customer 7 (buyer user 100) and routes PATCH /orders/{id}/status. The role
// matrix is NOT loaded in unit tests, so Can() resolves from the explicit
// Permissions map — deny-all for anyone without the exact grant, admin/owner
// always bypass. Each call gets a FRESH store: Confirm mutates the order, so
// reusing one setup across cases would 409 the second approver.
func statusGuardSetup(t *testing.T) (*Handler, *chi.Mux) {
	t.Helper()
	st := newFakeStore()
	st.orders[1] = &SalesOrder{ID: 1, CustomerID: 7, Status: "pending_approval"}
	svc := NewService(st, nil, nil)
	h := NewHandler(svc)
	resolver := func(_ context.Context, userID int64) (int64, bool, error) {
		if userID == 100 {
			return 7, true, nil
		}
		return 9, true, nil
	}
	h.WithCustomerResolver(resolver)
	r := chi.NewRouter()
	r.Patch("/orders/{id}/status", h.UpdateSalesOrderStatus)
	return h, r
}

func asStaffRole(userID int64, role auth.Role, perms map[string]bool) context.Context {
	u := &auth.User{ID: userID, Role: role}
	if perms != nil {
		p := make(auth.Permissions, len(perms))
		for k, v := range perms {
			p[k] = v
		}
		u.Permissions = p
	}
	return auth.ToContext(context.Background(), u, 1)
}

func patchStatusBody() *strings.Reader {
	return strings.NewReader(`{"status":"confirmed"}`)
}

// The 2 Nov flow-audit regression: PATCH /status used to accept "confirmed"
// from any .view/.update holder (route gate) with only a warehouse role check
// in the handler — a plain sales (or the order's own buyer, via the buyer
// carve-out) could self-approve an order the human gate exists for. The
// handler now requires sales.sales-orders.confirm on the confirmed branch,
// the same permission POST /{id}/confirm demands.
func TestPatchStatus_ConfirmRequiresConfirmPermission(t *testing.T) {
	for _, tc := range []struct {
		name string
		ctx  context.Context
		want int
	}{
		// 403: holds no sales.sales-orders.confirm.
		{"sales .view-only", asStaffRole(11, auth.RoleSales, map[string]bool{"sales.sales-orders.view": true}), http.StatusForbidden},
		{"cashier .view-only", asStaffRole(12, auth.RoleCashier, map[string]bool{"sales.sales-orders.view": true}), http.StatusForbidden},
		{"approver .update only", asStaffRole(15, auth.RoleSales, map[string]bool{"sales.sales-orders.update": true}), http.StatusForbidden},
		{"buyer own order", asBuyer(100), http.StatusForbidden},
		// 404, still refused: Service.Get hides draft/pending_approval orders
		// from warehouse before the handler's confirm branch is reached.
		{"warehouse .view-only", asStaffRole(14, auth.RoleWarehouse, map[string]bool{"sales.sales-orders.view": true}), http.StatusNotFound},
	} {
		_, r := statusGuardSetup(t)
		req := httptest.NewRequest(http.MethodPatch, "/orders/1/status", patchStatusBody()).WithContext(tc.ctx)
		rec := httptest.NewRecorder()
		r.ServeHTTP(rec, req)
		if rec.Code != tc.want {
			t.Errorf("%s: PATCH confirmed = %d, want %d", tc.name, rec.Code, tc.want)
		}
	}
}

func TestPatchStatus_ConfirmAllowedForConfirmHolders(t *testing.T) {
	for _, tc := range []struct {
		name string
		role auth.Role
	}{
		{"spv", auth.RoleSPV},
		{"bm", auth.RoleBM},
		{"admin bypass", auth.RoleAdmin},
	} {
		_, r := statusGuardSetup(t)
		perms := map[string]bool{"sales.sales-orders.confirm": true}
		ctx := asStaffRole(21, tc.role, perms)
		if tc.role == auth.RoleAdmin {
			ctx = asStaffRole(21, tc.role, nil) // bypass lives inside Can()
		}
		req := httptest.NewRequest(http.MethodPatch, "/orders/1/status", patchStatusBody()).WithContext(ctx)
		rec := httptest.NewRecorder()
		r.ServeHTTP(rec, req)
		if rec.Code != http.StatusOK {
			t.Errorf("%s: PATCH confirmed = %d, want 200", tc.name, rec.Code)
		}
	}
}
