package dailyreport

import (
	"errors"
	"net/http"
	"net/http/httptest"
	"testing"

	"system-altrak/internal/domain"
	"time"

	"github.com/gofiber/fiber/v2"
	"gorm.io/gorm"
)

type dailyReportServiceMock struct {
	fetchRecords []domain.OperationalRecord
	fetchErr     error
	revertErr    error
	excelBytes   []byte
	excelErr     error
	pdfBytes     []byte
	pdfErr       error

	fetchSearch string
	fetchFrom   string
	fetchTo     string
	fetchBranch uint
	fetchRole   string

	revertID     uint
	revertBranch uint
	revertRole   string

	excelSearch       string
	excelFrom         string
	excelTo           string
	excelCutoff       string
	excelCustomerMode string
	excelCustomers    []string
	excelBranch       uint
	excelRole         string
	excelSort         string

	pdfSearch       string
	pdfFrom         string
	pdfTo           string
	pdfCutoff       string
	pdfCustomerMode string
	pdfCustomers    []string
	pdfBranch       uint
	pdfRole         string
	pdfSort         string
}

func (m *dailyReportServiceMock) FetchHistoricalRecords(search, from, to string, branchID uint, role string) ([]domain.OperationalRecord, error) {
	m.fetchSearch = search
	m.fetchFrom = from
	m.fetchTo = to
	m.fetchBranch = branchID
	m.fetchRole = role
	if m.fetchErr != nil {
		return nil, m.fetchErr
	}
	return m.fetchRecords, nil
}

func (m *dailyReportServiceMock) RevertVerification(id uint, branchID uint, role string) error {
	m.revertID = id
	m.revertBranch = branchID
	m.revertRole = role
	return m.revertErr
}

func (m *dailyReportServiceMock) ExportExcel(search, from, to, cutoffStr string, customerMode string, customerNames []string, branchID uint, role string, sortMode string) ([]byte, error) {
	m.excelSearch = search
	m.excelFrom = from
	m.excelTo = to
	m.excelCutoff = cutoffStr
	m.excelCustomerMode = customerMode
	m.excelCustomers = append([]string(nil), customerNames...)
	m.excelBranch = branchID
	m.excelRole = role
	m.excelSort = sortMode
	if m.excelErr != nil {
		return nil, m.excelErr
	}
	return m.excelBytes, nil
}

func (m *dailyReportServiceMock) ExportPdf(search, from, to, cutoffStr string, customerMode string, customerNames []string, branchID uint, role string, sortMode string) ([]byte, error) {
	m.pdfSearch = search
	m.pdfFrom = from
	m.pdfTo = to
	m.pdfCutoff = cutoffStr
	m.pdfCustomerMode = customerMode
	m.pdfCustomers = append([]string(nil), customerNames...)
	m.pdfBranch = branchID
	m.pdfRole = role
	m.pdfSort = sortMode
	if m.pdfErr != nil {
		return nil, m.pdfErr
	}
	return m.pdfBytes, nil
}

func (m *dailyReportServiceMock) MarkAsExported(ids []uint, branchID uint, exportedBy uint) error {
	return nil
}

func (m *dailyReportServiceMock) GetLastArchivedAt(branchID uint) (*time.Time, error) {
	return nil, nil
}

func (m *dailyReportServiceMock) GetYearlySummary(year int, branchID uint) ([]byte, error) {
	return nil, nil
}

func buildDailyReportHandlerApp(h *Handler) *fiber.App {
	app := fiber.New()

	applyAuth := func(c *fiber.Ctx) {
		c.Locals("current_branch_id", uint(9))
		c.Locals("role", "admin")
	}

	app.Get("/history", func(c *fiber.Ctx) error {
		applyAuth(c)
		return h.ListHistoricalArchive(c)
	})
	app.Put("/unverify/:id", func(c *fiber.Ctx) error {
		applyAuth(c)
		return h.RevertVerification(c)
	})
	app.Get("/export/verified/excel", func(c *fiber.Ctx) error {
		applyAuth(c)
		return h.ExportExcel(c)
	})
	app.Get("/export/verified/pdf", func(c *fiber.Ctx) error {
		applyAuth(c)
		return h.ExportPdf(c)
	})
	app.Get("/preview/verified/pdf", func(c *fiber.Ctx) error {
		applyAuth(c)
		return h.PreviewPdf(c)
	})

	return app
}

func TestListHistoricalArchiveDelegatesFiltersAndScope(t *testing.T) {
	svc := &dailyReportServiceMock{fetchRecords: []domain.OperationalRecord{{PsoNo: "PSO-1"}}}
	h := NewHandler(svc)
	app := buildDailyReportHandlerApp(h)

	req := httptest.NewRequest(http.MethodGet, "/history?search=acme&from_verify=2026-04-01&to_verify=2026-04-30", 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.fetchSearch != "acme" || svc.fetchFrom != "2026-04-01" || svc.fetchTo != "2026-04-30" {
		t.Fatalf("unexpected forwarded filters: search=%q from=%q to=%q", svc.fetchSearch, svc.fetchFrom, svc.fetchTo)
	}
	if svc.fetchBranch != 9 || svc.fetchRole != "admin" {
		t.Fatalf("unexpected forwarded scope: branch=%d role=%q", svc.fetchBranch, svc.fetchRole)
	}
}

func TestListHistoricalArchiveReturnsInternalError(t *testing.T) {
	svc := &dailyReportServiceMock{fetchErr: errors.New("db down")}
	h := NewHandler(svc)
	app := buildDailyReportHandlerApp(h)

	req := httptest.NewRequest(http.MethodGet, "/history", 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 TestRevertVerificationRejectsInvalidID(t *testing.T) {
	svc := &dailyReportServiceMock{}
	h := NewHandler(svc)
	app := buildDailyReportHandlerApp(h)

	req := httptest.NewRequest(http.MethodPut, "/unverify/abc", 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 svc.revertID != 0 {
		t.Fatal("did not expect service RevertVerification call")
	}
}

func TestRevertVerificationMapsNotFoundTo404(t *testing.T) {
	svc := &dailyReportServiceMock{revertErr: gorm.ErrRecordNotFound}
	h := NewHandler(svc)
	app := buildDailyReportHandlerApp(h)

	req := httptest.NewRequest(http.MethodPut, "/unverify/17", nil)
	resp, err := app.Test(req)
	if err != nil {
		t.Fatalf("request failed: %v", err)
	}

	if resp.StatusCode != fiber.StatusNotFound {
		t.Fatalf("expected %d, got %d", fiber.StatusNotFound, resp.StatusCode)
	}
}

func TestRevertVerificationDelegatesScope(t *testing.T) {
	svc := &dailyReportServiceMock{}
	h := NewHandler(svc)
	app := buildDailyReportHandlerApp(h)

	req := httptest.NewRequest(http.MethodPut, "/unverify/21", 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.revertID != 21 || svc.revertBranch != 9 || svc.revertRole != "admin" {
		t.Fatalf("unexpected revert delegation: id=%d branch=%d role=%q", svc.revertID, svc.revertBranch, svc.revertRole)
	}
}

func TestExportExcelSetsDownloadHeadersAndDelegatesQuery(t *testing.T) {
	svc := &dailyReportServiceMock{excelBytes: []byte("xlsx")}
	h := NewHandler(svc)
	app := buildDailyReportHandlerApp(h)

	req := httptest.NewRequest(http.MethodGet, "/export/verified/excel?search=pt&from_verify=2026-04-01&to_verify=2026-04-30&cutoff=2026-04-30&customer_mode=include&customers=%5B%22PT%20Alpha%22%2C%22PT%20Beta%22%5D", 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 got := resp.Header.Get("Content-Type"); got != "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" {
		t.Fatalf("unexpected content type: %q", got)
	}
	if got := resp.Header.Get("Content-Disposition"); got != `attachment; filename="Daily-Verified-Report.xlsx"` {
		t.Fatalf("unexpected content disposition: %q", got)
	}
	if svc.excelSearch != "pt" || svc.excelFrom != "2026-04-01" || svc.excelTo != "2026-04-30" || svc.excelCutoff != "2026-04-30" {
		t.Fatalf("unexpected export excel query forwarding: search=%q from=%q to=%q cutoff=%q", svc.excelSearch, svc.excelFrom, svc.excelTo, svc.excelCutoff)
	}
	if svc.excelCustomerMode != "include" || len(svc.excelCustomers) != 2 || svc.excelCustomers[0] != "PT Alpha" || svc.excelCustomers[1] != "PT Beta" {
		t.Fatalf("unexpected export excel customer forwarding: mode=%q customers=%+v", svc.excelCustomerMode, svc.excelCustomers)
	}
}

func TestExportExcelReturnsInternalErrorOnFailure(t *testing.T) {
	svc := &dailyReportServiceMock{excelErr: errors.New("excel failed")}
	h := NewHandler(svc)
	app := buildDailyReportHandlerApp(h)

	req := httptest.NewRequest(http.MethodGet, "/export/verified/excel", 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 TestExportPdfSetsDownloadHeadersAndDelegatesQuery(t *testing.T) {
	svc := &dailyReportServiceMock{pdfBytes: []byte("pdf")}
	h := NewHandler(svc)
	app := buildDailyReportHandlerApp(h)

	req := httptest.NewRequest(http.MethodGet, "/export/verified/pdf?search=pt&from_verify=2026-04-01&to_verify=2026-04-30&cutoff=2026-04-30&customer_mode=exclude&customers=%5B%22PT%20Gamma%22%5D", 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 got := resp.Header.Get("Content-Type"); got != "application/pdf" {
		t.Fatalf("unexpected content type: %q", got)
	}
	if got := resp.Header.Get("Content-Disposition"); got != `attachment; filename="Daily-Verified-Report.pdf"` {
		t.Fatalf("unexpected content disposition: %q", got)
	}
	if svc.pdfSearch != "pt" || svc.pdfFrom != "2026-04-01" || svc.pdfTo != "2026-04-30" || svc.pdfCutoff != "2026-04-30" {
		t.Fatalf("unexpected export pdf query forwarding: search=%q from=%q to=%q cutoff=%q", svc.pdfSearch, svc.pdfFrom, svc.pdfTo, svc.pdfCutoff)
	}
	if svc.pdfCustomerMode != "exclude" || len(svc.pdfCustomers) != 1 || svc.pdfCustomers[0] != "PT Gamma" {
		t.Fatalf("unexpected export pdf customer forwarding: mode=%q customers=%+v", svc.pdfCustomerMode, svc.pdfCustomers)
	}
}

func TestPreviewPdfSetsInlineHeadersAndDelegatesQuery(t *testing.T) {
	svc := &dailyReportServiceMock{pdfBytes: []byte("pdf")}
	h := NewHandler(svc)
	app := buildDailyReportHandlerApp(h)

	req := httptest.NewRequest(http.MethodGet, "/preview/verified/pdf?search=pt&from_verify=2026-04-01&to_verify=2026-04-30&cutoff=2026-04-30&customer_mode=include&customers=%5B%22PT%20Alpha%22%5D", 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 got := resp.Header.Get("Content-Type"); got != "application/pdf" {
		t.Fatalf("unexpected content type: %q", got)
	}
	if got := resp.Header.Get("Content-Disposition"); got != `inline; filename="Daily-Verified-Report.pdf"` {
		t.Fatalf("unexpected content disposition: %q", got)
	}
	if svc.pdfSearch != "pt" || svc.pdfFrom != "2026-04-01" || svc.pdfTo != "2026-04-30" || svc.pdfCutoff != "2026-04-30" {
		t.Fatalf("unexpected preview pdf query forwarding: search=%q from=%q to=%q cutoff=%q", svc.pdfSearch, svc.pdfFrom, svc.pdfTo, svc.pdfCutoff)
	}
	if svc.pdfCustomerMode != "include" || len(svc.pdfCustomers) != 1 || svc.pdfCustomers[0] != "PT Alpha" {
		t.Fatalf("unexpected preview pdf customer forwarding: mode=%q customers=%+v", svc.pdfCustomerMode, svc.pdfCustomers)
	}
}

func TestExportPdfReturnsInternalErrorOnFailure(t *testing.T) {
	svc := &dailyReportServiceMock{pdfErr: errors.New("pdf failed")}
	h := NewHandler(svc)
	app := buildDailyReportHandlerApp(h)

	req := httptest.NewRequest(http.MethodGet, "/export/verified/pdf", 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)
	}
}
