package activity

import (
	"encoding/json"
	"errors"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"system-altrak/internal/domain"

	"github.com/gofiber/fiber/v2"
)

type activityServiceMock struct {
	listLogs      []domain.ActivityLog
	listErr       error
	dashboardResp *DashboardResponse
	dashboardErr  error
	restoreErr    error

	listCalls           int
	dashboardCalls      int
	restoreCalls        int
	lastBranchID        uint
	lastRole            string
	lastRestoreResource string
	lastRestoreID       uint
	logCalls            int
	lastLogBranch       uint
	lastLogAction       string
	lastLogDetails      string
}

func (m *activityServiceMock) List(branchID uint, role string) ([]domain.ActivityLog, error) {
	m.listCalls++
	m.lastBranchID = branchID
	m.lastRole = role
	if m.listErr != nil {
		return nil, m.listErr
	}
	return m.listLogs, nil
}

func (m *activityServiceMock) Dashboard(branchID uint, role string) (*DashboardResponse, error) {
	m.dashboardCalls++
	m.lastBranchID = branchID
	m.lastRole = role
	if m.dashboardErr != nil {
		return nil, m.dashboardErr
	}
	if m.dashboardResp != nil {
		return m.dashboardResp, nil
	}
	return &DashboardResponse{}, nil
}

func (m *activityServiceMock) Restore(resource string, id uint, branchID uint, role string) error {
	m.restoreCalls++
	m.lastRestoreResource = resource
	m.lastRestoreID = id
	m.lastBranchID = branchID
	m.lastRole = role
	return m.restoreErr
}

func (m *activityServiceMock) Log(userID uint, username, role, module, action, details, ip, userAgent string, branchID uint) error {
	m.logCalls++
	m.lastLogBranch = branchID
	m.lastLogAction = action
	m.lastLogDetails = details
	return nil
}

func buildActivityHandlerTestApp(h *Handler, withLocals bool, branchID uint, role string) *fiber.App {
	app := fiber.New()
	if withLocals {
		app.Use(func(c *fiber.Ctx) error {
			c.Locals("current_branch_id", branchID)
			c.Locals("role", role)
			return c.Next()
		})
	}
	app.Get("/activity", h.List)
	app.Get("/activity/dashboard", h.Dashboard)
	app.Post("/activity/restore", h.Restore)
	return app
}

func decodeActivityBody(t *testing.T, resp *http.Response) map[string]interface{} {
	t.Helper()
	var body map[string]interface{}
	if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
		t.Fatalf("decode response failed: %v", err)
	}
	return body
}

func TestListReturnsSuccessAndDelegatesContext(t *testing.T) {
	svc := &activityServiceMock{
		listLogs: []domain.ActivityLog{{Username: "alice", Action: "CREATE", BranchID: 3}},
	}
	h := NewHandler(svc)
	app := buildActivityHandlerTestApp(h, true, 3, "admin")

	req := httptest.NewRequest(http.MethodGet, "/activity", nil)
	resp, err := app.Test(req)
	if err != nil {
		t.Fatalf("request failed: %v", err)
	}
	if resp.StatusCode != fiber.StatusOK {
		t.Fatalf("expected %d, got %d", fiber.StatusOK, resp.StatusCode)
	}

	if svc.listCalls != 1 || svc.lastBranchID != 3 || svc.lastRole != "admin" {
		t.Fatalf("unexpected list args calls=%d branch=%d role=%q", svc.listCalls, svc.lastBranchID, svc.lastRole)
	}

	body := decodeActivityBody(t, resp)
	if body["success"] != true {
		t.Fatalf("expected success=true, got=%v", body["success"])
	}
	logs, ok := body["data"].([]interface{})
	if !ok || len(logs) != 1 {
		t.Fatalf("expected one activity log in data, got=%T len=%d", body["data"], len(logs))
	}
}

func TestListReturnsInternalErrorWhenServiceFails(t *testing.T) {
	svc := &activityServiceMock{listErr: errors.New("storage unavailable")}
	h := NewHandler(svc)
	app := buildActivityHandlerTestApp(h, true, 1, "user")

	req := httptest.NewRequest(http.MethodGet, "/activity", nil)
	resp, err := app.Test(req)
	if err != nil {
		t.Fatalf("request failed: %v", err)
	}
	if resp.StatusCode != fiber.StatusInternalServerError {
		t.Fatalf("expected %d, got %d", fiber.StatusInternalServerError, resp.StatusCode)
	}
	if svc.listCalls != 1 {
		t.Fatalf("expected one List call, got=%d", svc.listCalls)
	}
}

func TestListUsesZeroValuesWhenLocalsMissing(t *testing.T) {
	svc := &activityServiceMock{listLogs: []domain.ActivityLog{}}
	h := NewHandler(svc)
	app := buildActivityHandlerTestApp(h, false, 0, "")

	req := httptest.NewRequest(http.MethodGet, "/activity", nil)
	resp, err := app.Test(req)
	if err != nil {
		t.Fatalf("request failed: %v", err)
	}
	if resp.StatusCode != fiber.StatusOK {
		t.Fatalf("expected %d, got %d", fiber.StatusOK, resp.StatusCode)
	}
	if svc.lastBranchID != 0 || svc.lastRole != "" {
		t.Fatalf("expected zero-value context forwarding, got branch=%d role=%q", svc.lastBranchID, svc.lastRole)
	}
}

func TestDashboardReturnsSuccessAndDelegatesContext(t *testing.T) {
	svc := &activityServiceMock{dashboardResp: &DashboardResponse{Cards: []ModuleCard{{ModuleKey: ModuleKeyPOStatus, ModuleLabel: "PO Status", Total: 2}}}}
	h := NewHandler(svc)
	app := buildActivityHandlerTestApp(h, true, 9, "superadmin")

	req := httptest.NewRequest(http.MethodGet, "/activity/dashboard", nil)
	resp, err := app.Test(req)
	if err != nil {
		t.Fatalf("request failed: %v", err)
	}
	if resp.StatusCode != fiber.StatusOK {
		t.Fatalf("expected %d, got %d", fiber.StatusOK, resp.StatusCode)
	}
	if svc.dashboardCalls != 1 || svc.lastBranchID != 9 || svc.lastRole != "superadmin" {
		t.Fatalf("unexpected dashboard args calls=%d branch=%d role=%q", svc.dashboardCalls, svc.lastBranchID, svc.lastRole)
	}
}

func TestRestoreDelegatesRequestBody(t *testing.T) {
	svc := &activityServiceMock{}
	h := NewHandler(svc)
	app := buildActivityHandlerTestApp(h, true, 4, "admin")

	req := httptest.NewRequest(http.MethodPost, "/activity/restore", strings.NewReader(`{"resource":"customer_profile","id":12}`))
	req.Header.Set("Content-Type", "application/json")
	resp, err := app.Test(req)
	if err != nil {
		t.Fatalf("request failed: %v", err)
	}
	if resp.StatusCode != fiber.StatusOK {
		t.Fatalf("expected %d, got %d", fiber.StatusOK, resp.StatusCode)
	}
	if svc.restoreCalls != 1 || svc.lastRestoreResource != "customer_profile" || svc.lastRestoreID != 12 || svc.lastBranchID != 4 || svc.lastRole != "admin" {
		t.Fatalf("unexpected restore args calls=%d resource=%q id=%d branch=%d role=%q", svc.restoreCalls, svc.lastRestoreResource, svc.lastRestoreID, svc.lastBranchID, svc.lastRole)
	}
}
