package app

import (
	. "kjol/wasmruntime/vdom"
	ui "kjol/webui"
)

// Employee is a row in the table demo. Salary and Bonus are both money, so a
// calculated column has two numeric columns to combine ACROSS a row.
type Employee struct {
	Name   string
	Email  string
	Team   string
	Status string
	Salary string
	Bonus  string
	Rank   string
	Note   string
}

func employees() []any {
	rows := []Employee{
		{"Ada Lovelace", "ada@example.com", "Engineering", "active", "$1,200.50", "$150.00", "Item 2", "Wrote the first algorithm."},
		{"Alan Turing", "alan@example.com", "Research", "active", "$1,500.00", "$300.00", "Item 10", "Decidability, and the machine."},
		{"Grace Hopper", "grace@example.com", "Engineering", "inactive", "$980.00", "$0.00", "Item 1", "Found the first bug. Literally."},
		{"Katherine Johnson", "katherine@example.com", "Research", "active", "$1,340.00", "$220.00", "Item 3", "Orbital mechanics, by hand."},
		{"Margaret Hamilton", "margaret@example.com", "Engineering", "active", "$1,610.25", "$400.00", "Item 21", "Coined 'software engineering'."},
		{"Barbara Liskov", "barbara@example.com", "Research", "inactive", "$1,120.00", "$90.00", "Item 7", "The substitution principle."},
		{"Radia Perlman", "radia@example.com", "Networking", "active", "$1,275.75", "$180.00", "Item 12", "Spanning tree protocol."},
		{"Karen Sparck Jones", "karen@example.com", "Research", "active", "$1,050.00", "$60.00", "Item 5", "Inverse document frequency."},
		{"Frances Allen", "frances@example.com", "Engineering", "inactive", "$1,400.00", "$250.00", "Item 9", "Optimizing compilers."},
		{"Jean Bartik", "jean@example.com", "Engineering", "active", "$860.00", "$40.00", "Item 4", "Programmed the ENIAC."},
		{"Evelyn Boyd Granville", "evelyn@example.com", "Research", "active", "$1,180.00", "$130.00", "Item 15", "Trajectory analysis."},
		{"Annie Easley", "annie@example.com", "Networking", "inactive", "$990.00", "$75.00", "Item 6", "Rocket propulsion code."},
	}
	out := make([]any, len(rows))
	for i, r := range rows {
		out[i] = r
	}
	return out
}

func emp(row any) Employee { return row.(Employee) }

func tableColumns() []ui.AutoTableColumn {
	return []ui.AutoTableColumn{
		{
			Key: "name", DisplayName: "Name", Sortable: true, SortIdentifier: "Name",
			CSV: true, CSVValue: func(r any) string { return emp(r).Name },
			// No Toggleable: the name is what identifies a row, so it cannot be hidden.
			Cell: func(r any) *VNode { return ui.AutoTableTdLeft("text-ink", Text(emp(r).Name)) },
		},
		{
			Key: "email", DisplayName: "Email", Sortable: true, SortIdentifier: "Email",
			Toggleable: true, CSV: true, CSVValue: func(r any) string { return emp(r).Email },
			Cell: func(r any) *VNode { return ui.AutoTableTdLeft("text-ink-muted", Text(emp(r).Email)) },
		},
		{
			Key: "team", DisplayName: "Team", Sortable: true, SortIdentifier: "Team",
			Toggleable: true, CSV: true, CSVValue: func(r any) string { return emp(r).Team },
			Cell: func(r any) *VNode { return ui.AutoTableTdLeft("", Text(emp(r).Team)) },
		},
		{
			Key: "status", DisplayName: "Status", Sortable: true, SortIdentifier: "Status",
			Toggleable: true, CSV: true, CSVValue: func(r any) string { return emp(r).Status },
			Cell: func(r any) *VNode {
				color := ui.BadgeGreen
				if emp(r).Status != "active" {
					color = ui.BadgeNeutral
				}
				return ui.AutoTableTdLeft("", ui.Badge(ui.BadgeProps{Color: color}, Text(emp(r).Status)))
			},
		},
		{
			// SortTypeMoney parses "$1,200.50" as a number — a plain string sort would
			// put $1,200.50 before $980.00.
			Key: "salary", DisplayName: "Salary", DisplayPosition: ui.COL_POS_RIGHT,
			Sortable: true, SortIdentifier: "Salary", SortType: ui.SortTypeMoney,
			Toggleable: true, CSV: true, CSVValue: func(r any) string { return emp(r).Salary },
			Cell: func(r any) *VNode { return ui.AutoTableTdRight("tabular-nums", Text(emp(r).Salary)) },
		},
		{
			Key: "bonus", DisplayName: "Bonus", DisplayPosition: ui.COL_POS_RIGHT,
			Sortable: true, SortIdentifier: "Bonus", SortType: ui.SortTypeMoney,
			Toggleable: true, CSV: true, CSVValue: func(r any) string { return emp(r).Bonus },
			Cell: func(r any) *VNode { return ui.AutoTableTdRight("tabular-nums", Text(emp(r).Bonus)) },
		},
		{
			// SortTypeNumeric sorts "Item 2" before "Item 10".
			Key: "rank", DisplayName: "Rank", Sortable: true, SortIdentifier: "Rank",
			SortType: ui.SortTypeNumeric, Toggleable: true, HiddenByDefault: true,
			CSV: true, CSVValue: func(r any) string { return emp(r).Rank },
			Cell: func(r any) *VNode { return ui.AutoTableTdLeft("", Text(emp(r).Rank)) },
		},
	}
}

// newEmployeeTable builds the table controller.
//
// It is factored out of TablePage so a test can drive the very same table the page
// renders — the export test checks the bytes this exact configuration produces,
// rather than a second copy of it that could drift.
//
// The controller owns the search, sort, page, expansion and column state. Build it
// ONCE, never inside a render closure: rebuilding it per frame would reset every
// filter on each keystroke.

func newEmployeeTable(highlight *Signal[string]) *ui.AutoTableState {
	return ui.NewAutoTableState(tableColumns(), ui.AutoTableStateOptions{
		PerPage: 5,

		// The table PAGES ITSELF to wherever the highlighted row landed after
		// filtering and sorting.
		HighlightMatch: func(r any) bool {
			return highlight.Get() != "" && emp(r).Email == highlight.Get()
		},

		// Calculated columns come in two shapes, and the difference is the thing to
		// understand:
		//
		//   BASIC    — a function over OPERAND COLUMNS, combined ACROSS each row.
		//              Sum over [Salary, Bonus] is this row's salary + bonus. It does
		//              NOT total the column. Operands are column KEYS (SortIdentifier),
		//              and subtract/divide are binary and ORDERED.
		//
		//   ADVANCED — an Excel-style formula, which names columns by DISPLAY name:
		//              [Salary] is this row's cell, {Salary} is the whole column, and
		//              {Salary:1:ROW()} is everything up to this row — a running total.
		//
		// Either way they are evaluated against the FILTERED, SORTED rows, so filtering
		// re-runs them. (ToCalcNumber parses "$1,200.50" for you.)
		Calculated: []ui.UserCalculatedColumn{
			{
				// Basic: two columns, added together, per row.
				ID: "comp", DisplayName: "Total comp", Fn: ui.CALC_FN_SUM,
				Operands: []string{"Salary", "Bonus"},
				DataType: ui.CALC_TYPE_MONEY, DisplayPosition: ui.COL_POS_RIGHT,
			},
			{
				// Advanced: a formula.
				ID: "annual", DisplayName: "Annual", Fn: ui.CALC_FN_CUSTOM,
				Formula: "([Salary] + [Bonus]) * 12", DataType: ui.CALC_TYPE_MONEY,
				DisplayPosition: ui.COL_POS_RIGHT,
			},
			{
				// Advanced, and position-dependent: a running total down the page.
				ID: "running", DisplayName: "Running total", Fn: ui.CALC_FN_CUSTOM,
				Formula: "SUM({Salary:1:ROW()})", DataType: ui.CALC_TYPE_MONEY,
				DisplayPosition: ui.COL_POS_RIGHT,
			},
		},
		// A summary row goes the OTHER way: one column, aggregated DOWN the whole
		// filtered set — not just the page on screen. Basic mode does that with a
		// function + one operand; this one uses a formula for the same thing.
		SummaryRows: []ui.UserSummaryRow{
			{ID: "total", Label: "Total salary", Fn: ui.CALC_FN_SUM,
				Operands: []string{"Salary"}, DataType: ui.CALC_TYPE_MONEY},
			{ID: "avg", Label: "Average salary", Fn: ui.CALC_FN_CUSTOM,
				Formula: "AVERAGE({Salary})", DataType: ui.CALC_TYPE_MONEY},
		},

		Accordion: true,
		RowKey:    func(r any) string { return emp(r).Email },
		AccordionContent: func(r any) *VNode {
			return P(Attr("class", "px-4 py-2 text-sm text-ink-soft"), Text(emp(r).Note))
		},

		Columns: ui.AutoTableColumnOptions{
			Toggleable: true,
			Draggable:  true,
			Resizable:  true,
			StorageKey: "gowasm-example-employees",
		},
	})
}

const tableSnippet = `// A controller: built ONCE, next to your signals — never inside the render.
table := ui.NewAutoTableState([]ui.AutoTableColumn{
    {DisplayName: "Name",   SortIdentifier: "Name",   Sortable: true,
     Cell: func(r any) *VNode { return Text(r.(Employee).Name) }},
    {DisplayName: "Salary", SortIdentifier: "Salary", Sortable: true,
     SortType: ui.SortTypeNumeric,               // parses the currency: $980 < $1,200.50
     Cell: func(r any) *VNode { return Text(money(r.(Employee).Salary)) }},
    {DisplayName: "Rank", HiddenByDefault: true},
}, ui.AutoTableStateOptions{
    PerPage: 5,
    Columns: ui.AutoTableColumnOptions{
        StorageKey: "employees",   // order, widths, visibility — the user's, and persisted
    },
})

table.SetRows(employees())`

const formulaSnippet = `A COLUMN combines operands ACROSS one row:

    sum[Salary, Bonus]              ->  1200.50 + 150.00   = 1350.50   (per person)
    ([Salary] + [Bonus]) * 12       ->  the annualised figure
    SUM({Salary:1:ROW()})           ->  a running total, down the rows

A SUMMARY ROW aggregates ONE column DOWN the filtered rows:

    avg[Salary]                     ->  one number, printed in the footer`
