package sr

import (
	"bytes"
	"io"
	"testing"
	"time"

	"system-altrak/internal/domain"

	"github.com/xuri/excelize/v2"
)

type srRepoMock struct {
	list    []domain.ServiceRequisition
	byID    map[uint]*domain.ServiceRequisition
	listErr error
}

func (m *srRepoMock) Create(sr *domain.ServiceRequisition) error {
	return nil
}

func (m *srRepoMock) Update(sr *domain.ServiceRequisition, branchID uint, role string) error {
	return nil
}

func (m *srRepoMock) GetByID(id uint, branchID uint, role string) (*domain.ServiceRequisition, error) {
	if rec, ok := m.byID[id]; ok {
		return rec, nil
	}
	return nil, nil
}

func (m *srRepoMock) List(branchID uint, role string) ([]domain.ServiceRequisition, error) {
	if m.listErr != nil {
		return nil, m.listErr
	}
	return m.list, nil
}

func (m *srRepoMock) GetNextSequence(docType string, year, month int) (int, error) {
	return 1, nil
}

func (m *srRepoMock) Delete(id uint, branchID uint, role string) error {
	return nil
}

func openExcelFromReader(t *testing.T, r io.Reader) *excelize.File {
	t.Helper()

	b, err := io.ReadAll(r)
	if err != nil {
		t.Fatalf("failed reading excel stream: %v", err)
	}

	f, err := excelize.OpenReader(bytes.NewReader(b))
	if err != nil {
		t.Fatalf("failed opening excel stream: %v", err)
	}

	t.Cleanup(func() {
		_ = f.Close()
	})

	return f
}

func countDataRows(rows [][]string) int {
	count := 0
	for i, row := range rows {
		if i == 0 {
			continue
		}
		if len(row) == 0 {
			continue
		}
		if row[0] != "" {
			count++
		}
	}
	return count
}

func TestNormalizeSRStatus(t *testing.T) {
	tests := []struct {
		input    string
		expected string
	}{
		{input: "completed", expected: "submitted"},
		{input: "verified", expected: "approved"},
		{input: " APPROVED ", expected: "approved"},
		{input: "draft", expected: "draft"},
	}

	for _, tc := range tests {
		if got := normalizeSRStatus(tc.input); got != tc.expected {
			t.Fatalf("normalizeSRStatus(%q) = %q, want %q", tc.input, got, tc.expected)
		}
	}
}

func TestNormalizeSRTab(t *testing.T) {
	tests := []struct {
		input    string
		expected string
	}{
		{input: "draft", expected: "draft"},
		{input: "completed", expected: "completed"},
		{input: "verified", expected: "completed"},
		{input: "unknown", expected: "all"},
	}

	for _, tc := range tests {
		if got := normalizeSRTab(tc.input); got != tc.expected {
			t.Fatalf("normalizeSRTab(%q) = %q, want %q", tc.input, got, tc.expected)
		}
	}
}

func TestMatchesSRTab(t *testing.T) {
	if !matchesSRTab("verified", "completed") {
		t.Fatal("expected verified to match completed tab")
	}

	if !matchesSRTab("completed", "completed") {
		t.Fatal("expected completed legacy status to match completed tab")
	}

	if matchesSRTab("draft", "completed") {
		t.Fatal("did not expect draft to match completed tab")
	}
}

func TestExportSRToExcelFiltersByTabAndSearch(t *testing.T) {
	now := time.Now()
	repo := &srRepoMock{
		list: []domain.ServiceRequisition{
			{BaseModel: domain.BaseModel{ID: 1}, Ref: "REQ-001", Date: now, Customer: "Acme Mining", Status: "submitted", Description: "engine"},
			{BaseModel: domain.BaseModel{ID: 2}, Ref: "REQ-002", Date: now, Customer: "Beta Marine", Status: "approved", Description: "pump"},
			{BaseModel: domain.BaseModel{ID: 3}, Ref: "REQ-003", Date: now, Customer: "Gamma Rail", Status: "draft", Description: "filter"},
		},
	}

	svc := NewService(repo, nil)
	r, err := svc.ExportSRToExcel(1, "admin", "completed", "acme")
	if err != nil {
		t.Fatalf("ExportSRToExcel returned error: %v", err)
	}

	f := openExcelFromReader(t, r)
	rows, err := f.GetRows("Service_Requisition")
	if err != nil {
		t.Fatalf("GetRows returned error: %v", err)
	}

	if got := countDataRows(rows); got != 1 {
		t.Fatalf("expected 1 data row after completed+search filter, got %d", got)
	}

	if rows[1][0] != "001" {
		t.Fatalf("expected row number 001, got %q", rows[1][0])
	}
	if rows[1][2] != "REQ-001" {
		t.Fatalf("expected first data row ref REQ-001, got %q", rows[1][2])
	}
	if rows[1][4] != "INTERNAL SERVICE REQUISITION" {
		t.Fatalf("expected category label, got %q", rows[1][4])
	}
}

func TestExportSRToExcelVerifiedAliasIncludesSubmittedAndApproved(t *testing.T) {
	now := time.Now()
	repo := &srRepoMock{
		list: []domain.ServiceRequisition{
			{BaseModel: domain.BaseModel{ID: 11}, Ref: "REQ-011", Date: now, Customer: "A", Status: "submitted"},
			{BaseModel: domain.BaseModel{ID: 12}, Ref: "REQ-012", Date: now, Customer: "B", Status: "approved"},
			{BaseModel: domain.BaseModel{ID: 13}, Ref: "REQ-013", Date: now, Customer: "C", Status: "draft"},
		},
	}

	svc := NewService(repo, nil)
	r, err := svc.ExportSRToExcel(1, "admin", "verified", "")
	if err != nil {
		t.Fatalf("ExportSRToExcel returned error: %v", err)
	}

	f := openExcelFromReader(t, r)
	rows, err := f.GetRows("Service_Requisition")
	if err != nil {
		t.Fatalf("GetRows returned error: %v", err)
	}

	if got := countDataRows(rows); got != 2 {
		t.Fatalf("expected 2 data rows for verified alias, got %d", got)
	}
}
