package job

import (
	"encoding/json"
	"errors"
	"net/http"
	"net/http/httptest"
	"testing"

	"system-altrak/internal/domain"

	"github.com/gofiber/fiber/v2"
)

type jobServiceMock struct {
	activeJobs []domain.BackgroundJob
	activeErr  error

	historyJobs  []domain.BackgroundJob
	historyErr   error
	historyTotal int64

	activeCalls   int
	historyCalls  int
	activeBranch  uint
	historyBranch uint
	historyPage   int
	historyLimit  int
}

func (m *jobServiceMock) CreateJob(jobType, description string, totalItems int, branchID, userID uint) (*domain.BackgroundJob, error) {
	return nil, nil
}

func (m *jobServiceMock) StartJob(jobID uint) error {
	return nil
}

func (m *jobServiceMock) UpdateProgress(jobID uint, processed int, description string) error {
	return nil
}

func (m *jobServiceMock) CompleteJob(jobID uint) error {
	return nil
}

func (m *jobServiceMock) FailJob(jobID uint, message string) error {
	return nil
}

func (m *jobServiceMock) GetActiveJobs(branchID uint) ([]domain.BackgroundJob, error) {
	m.activeCalls++
	m.activeBranch = branchID
	if m.activeErr != nil {
		return nil, m.activeErr
	}
	return m.activeJobs, nil
}

func (m *jobServiceMock) ListJobs(page, limit int, branchID uint) ([]domain.BackgroundJob, int64, error) {
	m.historyCalls++
	m.historyPage = page
	m.historyLimit = limit
	m.historyBranch = branchID
	if m.historyErr != nil {
		return nil, 0, m.historyErr
	}
	return m.historyJobs, m.historyTotal, nil
}

func (m *jobServiceMock) GetJobByID(id uint) (*domain.BackgroundJob, error) {
	return nil, nil
}

func buildJobHandlerTestApp(h *Handler, branchID uint) *fiber.App {
	app := fiber.New()
	app.Use(func(c *fiber.Ctx) error {
		c.Locals("current_branch_id", branchID)
		return c.Next()
	})
	app.Get("/jobs/active", h.GetActiveJobs)
	app.Get("/jobs/history", h.ListHistoricalJobs)
	return app
}

func decodeJobResponseMap(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 TestGetActiveJobsReturnsSuccess(t *testing.T) {
	svc := &jobServiceMock{activeJobs: []domain.BackgroundJob{{JobType: "IMPORT", Status: "PROCESSING"}}}
	h := NewHandler(svc)
	app := buildJobHandlerTestApp(h, 5)

	req := httptest.NewRequest(http.MethodGet, "/jobs/active", 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.activeCalls != 1 || svc.activeBranch != 5 {
		t.Fatalf("expected GetActiveJobs called once with branch 5, got calls=%d branch=%d", svc.activeCalls, svc.activeBranch)
	}

	body := decodeJobResponseMap(t, resp)
	if body["success"] != true {
		t.Fatalf("expected success=true, got=%v", body["success"])
	}
}

func TestGetActiveJobsReturnsInternalError(t *testing.T) {
	svc := &jobServiceMock{activeErr: errors.New("storage down")}
	h := NewHandler(svc)
	app := buildJobHandlerTestApp(h, 2)

	req := httptest.NewRequest(http.MethodGet, "/jobs/active", 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)
	}
}

func TestListHistoricalJobsUsesDefaultPagination(t *testing.T) {
	svc := &jobServiceMock{historyJobs: []domain.BackgroundJob{{JobType: "SYNC", Status: "COMPLETED"}}, historyTotal: 25}
	h := NewHandler(svc)
	app := buildJobHandlerTestApp(h, 9)

	req := httptest.NewRequest(http.MethodGet, "/jobs/history", 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.historyCalls != 1 {
		t.Fatalf("expected ListJobs call count 1, got %d", svc.historyCalls)
	}
	if svc.historyPage != 1 || svc.historyLimit != 20 || svc.historyBranch != 9 {
		t.Fatalf("unexpected ListJobs args page=%d limit=%d branch=%d", svc.historyPage, svc.historyLimit, svc.historyBranch)
	}

	body := decodeJobResponseMap(t, resp)
	meta, ok := body["meta"].(map[string]interface{})
	if !ok {
		t.Fatalf("expected meta object, got=%T", body["meta"])
	}
	if meta["page"] != float64(1) || meta["limit"] != float64(20) {
		t.Fatalf("unexpected pagination meta: %+v", meta)
	}
}

func TestListHistoricalJobsUsesQueryPagination(t *testing.T) {
	svc := &jobServiceMock{historyJobs: []domain.BackgroundJob{}, historyTotal: 0}
	h := NewHandler(svc)
	app := buildJobHandlerTestApp(h, 11)

	req := httptest.NewRequest(http.MethodGet, "/jobs/history?page=3&limit=50", 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.historyPage != 3 || svc.historyLimit != 50 || svc.historyBranch != 11 {
		t.Fatalf("unexpected ListJobs args page=%d limit=%d branch=%d", svc.historyPage, svc.historyLimit, svc.historyBranch)
	}
}

func TestListHistoricalJobsReturnsInternalError(t *testing.T) {
	svc := &jobServiceMock{historyErr: errors.New("db timeout")}
	h := NewHandler(svc)
	app := buildJobHandlerTestApp(h, 1)

	req := httptest.NewRequest(http.MethodGet, "/jobs/history?page=2&limit=10", 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)
	}
}
