package sales

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

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

func TestDeleteOrderHandlerReturnsBodyless204(t *testing.T) {
	s := svc()
	warehouseID := int64(1)
	s.store.(*fakeStore).orders[1] = &SalesOrder{ID: 1, Status: "draft", WarehouseID: &warehouseID}
	r := chi.NewRouter()
	r.Delete("/sales-orders/{id}", NewHandler(s).DeleteOrder)
	res := httptest.NewRecorder()
	r.ServeHTTP(res, httptest.NewRequest(http.MethodDelete, "/sales-orders/1", nil))
	if res.Code != http.StatusNoContent || res.Body.Len() != 0 {
		t.Fatalf("delete = status %d body %q, want 204 with empty body", res.Code, res.Body.String())
	}
}

// TestUpdateSalesOrderStatusHandler_Transitions covers the ERP status lane:
// approved/confirmed delegate to Confirm (draft-only), cancelled/canceled
// delegate to Cancel, anything else is a 400 validation error.
func TestUpdateSalesOrderStatusHandler_Transitions(t *testing.T) {
	cases := []struct {
		name      string
		body      string
		wantCode  int
		wantStat  string // expected order status when wantCode == 200
	}{
		{name: "approved confirms draft", body: `{"status":"approved"}`, wantCode: 200, wantStat: "confirmed"},
		{name: "confirmed confirms draft", body: `{"status":"confirmed"}`, wantCode: 200, wantStat: "confirmed"},
		{name: "cancelled cancels draft", body: `{"status":"cancelled"}`, wantCode: 200, wantStat: "cancelled"},
		{name: "canceled alias cancels draft", body: `{"status":"canceled"}`, wantCode: 200, wantStat: "cancelled"},
		{name: "case and space normalized", body: `{"status":"  Approved "}`, wantCode: 200, wantStat: "confirmed"},
		{name: "illegal fulfilment jump from draft rejected", body: `{"status":"shipped"}`, wantCode: 409},
		{name: "unknown status rejected", body: `{"status":"banana"}`, wantCode: 400},
		{name: "empty status rejected", body: `{}`, wantCode: 400},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			s := svc()
			warehouseID := int64(1)
			s.store.(*fakeStore).orders[1] = &SalesOrder{ID: 1, Status: "draft", WarehouseID: &warehouseID}
			r := chi.NewRouter()
			r.Patch("/sales-orders/{id}/status", NewHandler(s).UpdateSalesOrderStatus)
			res := httptest.NewRecorder()
			req := httptest.NewRequest(http.MethodPatch, "/sales-orders/1/status", strings.NewReader(tc.body))
			req.Header.Set("Content-Type", "application/json")
			// Production always reaches this handler behind auth middleware, so
			// the contract test carries a matching identity: an approver with
			// sales.sales-orders.confirm (admin role also satisfies the
			// fulfillment branch's staff check for the shipped-from-draft case).
			approver := &auth.User{ID: 7, Role: auth.RoleAdmin, Permissions: auth.Permissions{"sales.sales-orders.confirm": true}}
			req = req.WithContext(auth.ToContext(context.Background(), approver, 1))
			r.ServeHTTP(res, req)
			if res.Code != tc.wantCode {
				t.Fatalf("status = %d body %s, want %d", res.Code, res.Body.String(), tc.wantCode)
			}
			if tc.wantCode != 200 {
				return
			}
			var out struct {
				Data SalesOrder `json:"data"`
			}
			if err := json.Unmarshal(res.Body.Bytes(), &out); err != nil {
				t.Fatal(err)
			}
			if out.Data.Status != tc.wantStat {
				t.Errorf("order status = %q, want %q", out.Data.Status, tc.wantStat)
			}
		})
	}
}

// TestUpdateSalesOrderStatusHandler_IllegalTransitions verifies the service
// transition rules surface as 409 BUSINESS_RULE_VIOLATION through the handler:
// confirm only from draft; cancel blocked once delivered or cancelled.
func TestUpdateSalesOrderStatusHandler_IllegalTransitions(t *testing.T) {
	cases := []struct {
		name   string
		status string
		body   string
	}{
		{name: "confirm on already-confirmed", status: "confirmed", body: `{"status":"approved"}`},
		{name: "cancel on cancelled", status: "cancelled", body: `{"status":"cancelled"}`},
		{name: "cancel on delivered", status: "delivered", body: `{"status":"canceled"}`},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			s := svc()
			warehouseID := int64(1)
			s.store.(*fakeStore).orders[1] = &SalesOrder{ID: 1, Status: tc.status, WarehouseID: &warehouseID}
			r := chi.NewRouter()
			r.Patch("/sales-orders/{id}/status", NewHandler(s).UpdateSalesOrderStatus)
			res := httptest.NewRecorder()
			req := httptest.NewRequest(http.MethodPatch, "/sales-orders/1/status", strings.NewReader(tc.body))
			req.Header.Set("Content-Type", "application/json")
			// Confirm cases now require the confirm permission (2 Nov gate);
			// admin carries it via bypass and satisfies every branch here.
			approver := &auth.User{ID: 7, Role: auth.RoleAdmin}
			req = req.WithContext(auth.ToContext(context.Background(), approver, 1))
			r.ServeHTTP(res, req)
			if res.Code != http.StatusConflict {
				t.Fatalf("status = %d body %s, want 409", res.Code, res.Body.String())
			}
			var out struct {
				Error struct {
					Code string `json:"code"`
				} `json:"error"`
			}
			if err := json.Unmarshal(res.Body.Bytes(), &out); err != nil {
				t.Fatal(err)
			}
			if out.Error.Code != "BUSINESS_RULE_VIOLATION" {
				t.Errorf("error code = %q, want BUSINESS_RULE_VIOLATION", out.Error.Code)
			}
		})
	}
}

// TestUpdateSalesOrderStatusHandler_MissingOrder ensures a nonexistent id maps
// to 404 rather than a validation error.
func TestUpdateSalesOrderStatusHandler_MissingOrder(t *testing.T) {
	s := svc()
	r := chi.NewRouter()
	r.Patch("/sales-orders/{id}/status", NewHandler(s).UpdateSalesOrderStatus)
	res := httptest.NewRecorder()
	req := httptest.NewRequest(http.MethodPatch, "/sales-orders/99/status", strings.NewReader(`{"status":"approved"}`))
	req.Header.Set("Content-Type", "application/json")
	r.ServeHTTP(res, req)
	if res.Code != http.StatusNotFound {
		t.Fatalf("status = %d body %s, want 404", res.Code, res.Body.String())
	}
}
