package serviceparts

import (
	"bytes"
	"errors"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"system-altrak/internal/domain"

	"github.com/gofiber/fiber/v2"
)

type partsServiceMock struct {
	exportCalled bool
	lastTab      string
	lastSearch   string
	exportErr    error
	createCalled bool
	updateCalled bool
	lastCreate   *domain.MemoServiceParts
	lastUpdate   *domain.MemoServiceParts
}

func (m *partsServiceMock) CreateAuthorization(serviceparts *domain.MemoServiceParts, branchID uint) error {
	m.createCalled = true
	copy := *serviceparts
	m.lastCreate = &copy
	return nil
}

func (m *partsServiceMock) UpdateAuthorization(serviceparts *domain.MemoServiceParts, branchID uint, role string) error {
	m.updateCalled = true
	copy := *serviceparts
	m.lastUpdate = &copy
	return nil
}

func (m *partsServiceMock) GetServiceParts(id uint, branchID uint, role string) (*domain.MemoServiceParts, error) {
	return nil, nil
}

func (m *partsServiceMock) ListServiceParts(branchID uint, role string) ([]domain.MemoServiceParts, error) {
	return nil, nil
}

func (m *partsServiceMock) ExportSRToExcel(branchID uint, role string, tab string, search string) (io.Reader, error) {
	m.exportCalled = true
	m.lastTab = tab
	m.lastSearch = search
	if m.exportErr != nil {
		return nil, m.exportErr
	}
	return bytes.NewBufferString("dummy-xlsx"), nil
}

func (m *partsServiceMock) GeneratePDF(id uint, branchID uint, role string) ([]byte, string, error) {
	return nil, "", nil
}

func (m *partsServiceMock) DeleteServiceParts(id uint, branchID uint, role string) error {
	return nil
}

func buildSRHandlerTestApp(h *Handler) *fiber.App {
	app := fiber.New()
	app.Post("/sr", func(c *fiber.Ctx) error {
		c.Locals("current_branch_id", uint(1))
		return h.CreateServiceParts(c)
	})
	app.Put("/sr/:id", func(c *fiber.Ctx) error {
		c.Locals("current_branch_id", uint(1))
		c.Locals("role", "admin")
		return h.UpdateServiceParts(c)
	})
	app.Get("/sr/export", func(c *fiber.Ctx) error {
		c.Locals("current_branch_id", uint(1))
		c.Locals("role", "admin")
		return h.ExportExcel(c)
	})
	return app
}

func TestHandlerExportExcelRejectsInvalidTab(t *testing.T) {
	mockSvc := &partsServiceMock{}
	h := NewHandler(mockSvc)
	app := buildSRHandlerTestApp(h)

	req := httptest.NewRequest(http.MethodGet, "/sr/export?tab=broken", nil)
	resp, err := app.Test(req)
	if err != nil {
		t.Fatalf("request failed: %v", err)
	}

	if resp.StatusCode != fiber.StatusBadRequest {
		t.Fatalf("expected %d, got %d", fiber.StatusBadRequest, resp.StatusCode)
	}

	if mockSvc.exportCalled {
		t.Fatal("did not expect service export call for invalid tab")
	}
}

func TestHandlerExportExcelMapsVerifiedAliasAndPassesSearch(t *testing.T) {
	mockSvc := &partsServiceMock{}
	h := NewHandler(mockSvc)
	app := buildSRHandlerTestApp(h)

	req := httptest.NewRequest(http.MethodGet, "/sr/export?tab=verified&search=acme", 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 !mockSvc.exportCalled {
		t.Fatal("expected service export call")
	}

	if mockSvc.lastTab != "completed" {
		t.Fatalf("expected tab completed, got %q", mockSvc.lastTab)
	}

	if mockSvc.lastSearch != "acme" {
		t.Fatalf("expected search acme, got %q", mockSvc.lastSearch)
	}
}

func TestHandlerExportExcelReturnsInternalErrorWhenServiceFails(t *testing.T) {
	mockSvc := &partsServiceMock{exportErr: errors.New("xlsx export failed")}
	h := NewHandler(mockSvc)
	app := buildSRHandlerTestApp(h)

	req := httptest.NewRequest(http.MethodGet, "/sr/export?tab=all", 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 TestHandlerCreateServicePartsMapsPayloadWithoutRef(t *testing.T) {
	mockSvc := &partsServiceMock{}
	h := NewHandler(mockSvc)
	app := buildSRHandlerTestApp(h)

	body := `{"charge_to":"Account ID","cc":"DWI HANDOKO","customer":"PT. LAGUNA MANDIRI JAYA","equipment_location":"Site A","spjb_no":"SPJB-123","description":"Pengecekan Unit","status":"submitted","checklist":{"pre_delivery_check":true,"other":true},"equipments":[{"no":1,"brand":"CUMMINS","model":"QSK60","serial_number":"3890756","qty":1,"mhp_no":"MHP-1","table_block":1}]}`
	req := httptest.NewRequest(http.MethodPost, "/sr", strings.NewReader(body))
	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 !mockSvc.createCalled {
		t.Fatal("expected service create call")
	}

	if mockSvc.lastCreate == nil {
		t.Fatal("expected captured create payload")
	}

	if mockSvc.lastCreate.Ref != "" {
		t.Fatalf("expected empty ref before service generation, got %q", mockSvc.lastCreate.Ref)
	}

	if mockSvc.lastCreate.SalesAgreementNo != "SPJB-123" {
		t.Fatalf("expected spjb mapping, got %q", mockSvc.lastCreate.SalesAgreementNo)
	}

	if mockSvc.lastCreate.Customer != "PT. LAGUNA MANDIRI JAYA" {
		t.Fatalf("expected mapped customer, got %q", mockSvc.lastCreate.Customer)
	}

	if mockSvc.lastCreate.Checklist == nil || !mockSvc.lastCreate.Checklist.PreDeliveryCheck || !mockSvc.lastCreate.Checklist.Other {
		t.Fatal("expected checklist to be mapped")
	}

	if len(mockSvc.lastCreate.Equipments) != 1 || mockSvc.lastCreate.Equipments[0].Brand != "CUMMINS" {
		t.Fatal("expected equipment row to be mapped")
	}
}

func TestHandlerUpdateServicePartsMapsPayloadWithoutRef(t *testing.T) {
	mockSvc := &partsServiceMock{}
	h := NewHandler(mockSvc)
	app := buildSRHandlerTestApp(h)

	body := `{"charge_to":"Account ID","cc":"DWI HANDOKO","customer":"PT. LAGUNA MANDIRI JAYA","equipment_location":"Site A","spjb_no":"SPJB-456","description":"Pengecekan Unit","status":"submitted","checklist":{"repair":true},"equipments":[{"no":1,"brand":"CUMMINS","model":"QSK60","serial_number":"3890756","qty":2,"mhp_no":"MHP-2","table_block":1}]}`
	req := httptest.NewRequest(http.MethodPut, "/sr/42", strings.NewReader(body))
	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 !mockSvc.updateCalled {
		t.Fatal("expected service update call")
	}

	if mockSvc.lastUpdate == nil {
		t.Fatal("expected captured update payload")
	}

	if mockSvc.lastUpdate.ID != 42 {
		t.Fatalf("expected update id 42, got %d", mockSvc.lastUpdate.ID)
	}

	if mockSvc.lastUpdate.Ref != "" {
		t.Fatalf("expected empty ref before service generation, got %q", mockSvc.lastUpdate.Ref)
	}

	if mockSvc.lastUpdate.SalesAgreementNo != "SPJB-456" {
		t.Fatalf("expected spjb mapping, got %q", mockSvc.lastUpdate.SalesAgreementNo)
	}

	if mockSvc.lastUpdate.Checklist == nil || !mockSvc.lastUpdate.Checklist.Repair {
		t.Fatal("expected checklist update to be mapped")
	}

	if len(mockSvc.lastUpdate.Equipments) != 1 || mockSvc.lastUpdate.Equipments[0].Qty != 2 {
		t.Fatal("expected equipment update row to be mapped")
	}
}
