package tests

import (
	"bytes"
	"fmt"
	"strconv"
	"testing"

	"github.com/olekukonko/tablewriter"
	"github.com/olekukonko/tablewriter/renderer"
	"github.com/olekukonko/tablewriter/tw"
)

// Employee represents a struct for employee data, simulating a database record.
type Employee struct {
	ID         int
	Name       string
	Age        int
	Department string
	Salary     float64
}

// dummyDatabase simulates a database with employee records.
type dummyDatabase struct {
	records []Employee
}

// fetchEmployees simulates fetching data from a database.
func (db *dummyDatabase) fetchEmployees() []Employee {
	return db.records
}

// employeeStringer converts an Employee struct to a slice of strings for table rendering.
func employeeStringer(e interface{}) []string {
	emp, ok := e.(Employee)
	if !ok {
		return []string{"Error: Invalid type"}
	}
	return []string{
		strconv.Itoa(emp.ID),
		emp.Name,
		strconv.Itoa(emp.Age),
		emp.Department,
		strconv.FormatFloat(emp.Salary, 'f', 2, 64),
	}
}

// TestStructTableWithDB tests rendering a table from struct data fetched from a dummy database.
func TestStructTableWithDB(t *testing.T) {
	// Initialize dummy database with sample data
	db := &dummyDatabase{
		records: []Employee{
			{ID: 1, Name: "Alice Smith", Age: 28, Department: "Engineering", Salary: 75000.50},
			{ID: 2, Name: "Bob Johnson", Age: 34, Department: "Marketing", Salary: 62000.00},
			{ID: 3, Name: "Charlie Brown", Age: 45, Department: "HR", Salary: 80000.75},
		},
	}

	// Configure table with custom settings
	config := tablewriter.Config{
		Header: tw.CellConfig{
			Formatting: tw.CellFormatting{
				AutoFormat: tw.On,
			},
			Alignment: tw.CellAlignment{Global: tw.AlignCenter},
		},
		Row: tw.CellConfig{
			Alignment: tw.CellAlignment{Global: tw.AlignLeft},
		},
		Footer: tw.CellConfig{
			Alignment: tw.CellAlignment{Global: tw.AlignRight},
		},
	}

	// Create table with buffer and custom renderer
	var buf bytes.Buffer
	table := tablewriter.NewTable(&buf,
		tablewriter.WithConfig(config),
		tablewriter.WithRenderer(renderer.NewBlueprint(tw.Rendition{
			Symbols: tw.NewSymbols(tw.StyleRounded), // Use rounded Unicode style
			Settings: tw.Settings{
				Separators: tw.Separators{
					BetweenColumns: tw.On,
					BetweenRows:    tw.Off,
				},
				Lines: tw.Lines{
					ShowHeaderLine: tw.On,
				},
			},
		})),
		tablewriter.WithStringer(employeeStringer),
	)

	// Set header
	table.Header([]string{"ID", "Name", "Age", "Department", "Salary"})

	// Fetch data from "database" and append to table
	employees := db.fetchEmployees()
	for _, emp := range employees {
		if err := table.Append(emp); err != nil {
			t.Fatalf("Failed to append employee: %v", err)
		}
	}

	// Add a footer with a total salary
	totalSalary := 0.0
	for _, emp := range employees {
		totalSalary += emp.Salary
	}
	table.Footer([]string{"", "", "", "Total", fmt.Sprintf("%.2f", totalSalary)})

	// Render the table
	if err := table.Render(); err != nil {
		t.Fatalf("Failed to render table: %v", err)
	}

	// Expected output
	expected := `
        ╭────┬───────────────┬─────┬─────────────┬───────────╮
        │ ID │     NAME      │ AGE │ DEPARTMENT  │  SALARY   │
        ├────┼───────────────┼─────┼─────────────┼───────────┤
        │ 1  │ Alice Smith   │ 28  │ Engineering │ 75000.50  │
        │ 2  │ Bob Johnson   │ 34  │ Marketing   │ 62000.00  │
        │ 3  │ Charlie Brown │ 45  │ HR          │ 80000.75  │
        ├────┼───────────────┼─────┼─────────────┼───────────┤
        │    │               │     │       Total │ 217001.25 │
        ╰────┴───────────────┴─────┴─────────────┴───────────╯
`

	// Visual check
	if !visualCheck(t, "StructTableWithDB", buf.String(), expected) {
		t.Log(table.Debug())
	}
}

func TestAutoHeaderScenarios(t *testing.T) {
	// Legacy/Standard Structs
	type Basic struct {
		Foo int
		Bar string
		baz bool // unexported
	}

	type WithTags struct {
		ID   int    `json:"id"`
		Name string `json:"name,omitempty"`
		Age  int    `json:"-"`
		City string `json:"location"`
	}

	type Omitted struct {
		SkipMe string `json:"-"`
		KeepMe string
	}

	type NoTags struct {
		Field1 string
		field2 int // unexported
	}

	type Embedded struct {
		WithTags
		Extra string
	}

	type PointerTest struct {
		Value string
	}

	// Alignment & Header Alignment Tests
	type TWAlign struct {
		LeftCol   string `tw:"name=Left Col, align=left, header_align=center"`
		CenterCol string `tw:"name=Center Col, align=center, header_align=right"`
		RightCol  string `tw:"name=Right Col, align=right, header_align=left"`
	}

	// Advanced Padding Tests
	type TWPadding struct {
		PadA string `tw:"name=Pad A, pad_left=>, pad_right=<"`
		PadB string `tw:"name=Pad B, pad_left=-, pad_right=-"`
		PadC string `tw:"name=Pad C, pad_left=, pad_right=*"`
	}

	// Per-Column Width Tests
	type TWPerColumnMaxWidth struct {
		Narrow string `tw:"name=Narrow, max_width=8, wrap=truncate"`
		Medium string `tw:"name=Medium, max_width=12, wrap=truncate"`
		Wide   string `tw:"name=Wide, max_width=16, wrap=truncate"`
	}

	// Table-Wide Formatting Overrides
	type TWTableWideOverrides struct {
		Raw   string `tw:"name=my_raw_header, auto_format=false"`
		Space string `tw:"name=Spaced, trim_space=false"`
		Tab   string `tw:"name=Tabbed, trim_tab=false"`
	}

	// Tag Fallbacks & Composites
	type TWComposite struct {
		A string `json:"json_name" tw:"align=right"`
		B string `db:"db_name" tw:"-"`
		C string `json:"ignore_me" tw:"name=tw_name"`
	}

	tests := []struct {
		name       string
		data       interface{}
		enable     bool
		preHeaders []string
		expected   string
	}{
		// Legacy Tests
		{
			name:   "BasicStruct",
			data:   []Basic{{1, "test", true}, {2, "test2", false}},
			enable: true,
			expected: `
			┌─────┬───────┐
			│ FOO │  BAR  │
			├─────┼───────┤
			│ 1   │ test  │
			│ 2   │ test2 │
			└─────┴───────┘
`,
		},
		{
			name:   "WithTags",
			data:   []WithTags{{1, "John", 30, "NY"}, {2, "", 0, "LA"}},
			enable: true,
			expected: `
			┌────┬──────┬──────────┐
			│ ID │ NAME │ LOCATION │
			├────┼──────┼──────────┤
			│ 1  │ John │ NY       │
			│ 2  │      │ LA       │
			└────┴──────┴──────────┘
`,
		},
		{
			name:   "OmittedFields",
			data:   []Omitted{{"skip", "keep"}, {"skip2", "keep2"}},
			enable: true,
			expected: `
			┌────────┐
			│ KEEPME │
			├────────┤
			│ keep   │
			│ keep2  │
			└────────┘
`,
		},
		{
			name:   "NoTags",
			data:   []NoTags{{"val1", 42}, {"val2", 43}},
			enable: true,
			expected: `
			┌─────────┐
			│ FIELD 1 │
			├─────────┤
			│ val1    │
			│ val2    │
			└─────────┘
`,
		},
		{
			name:   "Embedded",
			data:   []Embedded{{WithTags{1, "John", 30, "NY"}, "Extra"}, {WithTags{2, "Doe", 25, "LA"}, "Value"}},
			enable: true,
			expected: `
		┌────┬──────┬──────────┬───────┐
		│ ID │ NAME │ LOCATION │ EXTRA │
		├────┼──────┼──────────┼───────┤
		│ 1  │ John │ NY       │ Extra │
		│ 2  │ Doe  │ LA       │ Value │
		└────┴──────┴──────────┴───────┘
`,
		},
		{
			name:   "PointerToStruct",
			data:   []*PointerTest{{"Value1"}, {"Value2"}},
			enable: true,
			expected: `
			┌────────┐
			│ VALUE  │
			├────────┤
			│ Value1 │
			│ Value2 │
			└────────┘
`,
		},
		{
			name:   "SliceOfPointers",
			data:   []*WithTags{{1, "John", 30, "NY"}, {2, "Doe", 25, "LA"}},
			enable: true,
			expected: `
			┌────┬──────┬──────────┐
			│ ID │ NAME │ LOCATION │
			├────┼──────┼──────────┤
			│ 1  │ John │ NY       │
			│ 2  │ Doe  │ LA       │
			└────┴──────┴──────────┘
`,
		},
		{
			name:   "NonStruct",
			data:   [][]string{{"A", "B"}, {"C", "D"}},
			enable: false,
			expected: `
┌───┬───┐
│ A │ B │
│ C │ D │
└───┴───┘
`,
		},
		{
			name:     "EmptySlice",
			data:     []WithTags{},
			expected: ``,
		},
		{
			name:   "enabled",
			data:   []WithTags{{1, "John", 30, "NY"}},
			enable: true,
			expected: `
			┌────┬──────┬──────────┐
			│ ID │ NAME │ LOCATION │
			├────┼──────┼──────────┤
			│ 1  │ John │ NY       │
			└────┴──────┴──────────┘
`,
		},
		{
			name:   "Disabled",
			data:   []WithTags{{1, "John", 30, "NY"}},
			enable: false,
			expected: `
			┌───┬──────┬────┐
			│ 1 │ John │ NY │
			└───┴──────┴────┘
`,
		},
		{
			name:       "PreExistingHeaders",
			data:       []WithTags{{1, "John", 30, "NY"}},
			preHeaders: []string{"CustomID", "CustomName", "CustomCity"},
			enable:     true,
			expected: `
            ┌───────────┬─────────────┬─────────────┐
            │ CUSTOM ID │ CUSTOM NAME │ CUSTOM CITY │
            ├───────────┼─────────────┼─────────────┤
            │ 1         │ John        │ NY          │
            └───────────┴─────────────┴─────────────┘
`,
		},

		// New 'tw' Tag Features Tests

		{
			name:   "TW_Alignment_Independence",
			data:   []TWAlign{{"A", "B", "C"}},
			enable: true,
			expected: `
			┌──────────┬────────────┬───────────┐
			│ LEFT COL │ CENTER COL │ RIGHT COL │
			├──────────┼────────────┼───────────┤
			│ A        │     B      │         C │
			└──────────┴────────────┴───────────┘
`,
		},
		{
			name:   "TW_Advanced_Padding",
			data:   []TWPadding{{"1", "2", "3"}},
			enable: true,
			expected: `
			┌───────┬───────┬──────┐
			│>PAD A<│-PAD B-│PAD C*│
			├───────┼───────┼──────┤
			│>1<<<<<│-2-----│3*****│
			└───────┴───────┴──────┘
`,
		},
		{
			name:   "TW_Per_Column_Max_Width",
			data:   []TWPerColumnMaxWidth{{"LongTextHere", "LongTextHere", "LongTextHere"}},
			enable: true,
			expected: `
			┌────────┬───────────┬──────────────┐
			│ NARROW │  MEDIUM   │     WIDE     │
			├────────┼───────────┼──────────────┤
			│ Long…  │ LongText… │ LongTextHere │
			└────────┴───────────┴──────────────┘
`,
		},
		{
			name:   "TW_Table_Wide_Formatting_Overrides",
			data:   []TWTableWideOverrides{{"data", "  spaced  ", "\ttabbed\t"}},
			enable: true,
			expected: `
			┌───────────────┬────────────┬────────────────┐
			│ my_raw_header │   Spaced   │     Tabbed     │
			├───────────────┼────────────┼────────────────┤
			│ data          │   spaced   │     tabbed     │
			└───────────────┴────────────┴────────────────┘
`,
		},
		{
			name:   "TW_Tag_Fallback_And_Composites",
			data:   []TWComposite{{"A", "B", "C"}},
			enable: true,
			expected: `
			┌───────────┬─────────┐
			│ JSON NAME │ TW NAME │
			├───────────┼─────────┤
			│         A │ C       │
			└───────────┴─────────┘
`,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			var buf bytes.Buffer
			table := tablewriter.NewTable(&buf)

			// Always enable auto header for these tests
			if tt.enable {
				table.Configure(func(cfg *tablewriter.Config) {
					cfg.Behavior.Structs.AutoHeader = tw.On
				})
			}

			if len(tt.preHeaders) > 0 {
				table.Header(tt.preHeaders)
			}

			err := table.Bulk(tt.data)
			if err != nil {
				t.Fatalf("Bulk failed: %v", err)
			}
			table.Render()

			if !visualCheck(t, tt.name, buf.String(), tt.expected) {
				t.Logf("Failed visual check. Output generated:\n%s", buf.String())
			}
		})
	}
}

func TestTwTagMaxWidthAppliesOnFreshTable(t *testing.T) {
	type Row struct {
		Col string `tw:"max_width=5"`
	}

	var buf bytes.Buffer
	table := tablewriter.NewTable(&buf)
	table.Configure(func(cfg *tablewriter.Config) {
		cfg.Behavior.Structs.AutoHeader = tw.On
	})

	if err := table.Bulk([]Row{{"abcdefgh"}}); err != nil {
		t.Fatalf("Bulk failed: %v", err)
	}

	cfg := table.Config()
	width, ok := cfg.Row.ColMaxWidths.PerColumn.OK(0)
	if !ok || width != 5 {
		t.Fatalf("expected Row.ColMaxWidths.PerColumn[0] == 5, got ok=%v width=%v (max_width tag did not apply)", ok, width)
	}
}

func TestTwTagSkipDoesNotLeakConfigToNextColumn(t *testing.T) {
	type Row struct {
		Hidden string `tw:"align=right,-"`
		Kept   string
	}

	var buf bytes.Buffer
	table := tablewriter.NewTable(&buf)
	table.Configure(func(cfg *tablewriter.Config) {
		cfg.Behavior.Structs.AutoHeader = tw.On
	})

	if err := table.Bulk([]Row{{"x", "y"}}); err != nil {
		t.Fatalf("Bulk failed: %v", err)
	}

	cfg := table.Config()
	for i, a := range cfg.Row.Alignment.PerColumn {
		if a == tw.AlignRight {
			t.Fatalf("column %d unexpectedly inherited the skipped field's right alignment: %#v", i, cfg.Row.Alignment.PerColumn)
		}
	}
}
