package boilingcore

import (
	"reflect"
	"testing"

	"github.com/aarondl/sqlboiler/v4/drivers"
)

func TestConfig_OutputDirDepth(t *testing.T) {
	t.Parallel()

	tests := []struct {
		Output string
		Depth  int
	}{
		{
			Output: ".",
			Depth:  0,
		},
		{
			Output: "./",
			Depth:  0,
		},
		{
			Output: "foo",
			Depth:  1,
		},
		{
			Output: "foo/bar",
			Depth:  2,
		},
	}

	for i, test := range tests {
		cfg := Config{
			OutFolder: test.Output,
		}
		if want, got := test.Depth, cfg.OutputDirDepth(); got != want {
			t.Errorf("%d) wrong depth, want: %d, got: %d", i, want, got)
		}
	}
}

func TestConvertAliases(t *testing.T) {
	t.Parallel()

	var intf any = map[string]any{
		"tables": map[string]any{
			"table_name": map[string]any{
				"up_plural":     "a",
				"up_singular":   "b",
				"down_plural":   "c",
				"down_singular": "d",

				"columns": map[string]any{
					"a": "b",
				},
				"relationships": map[string]any{
					"ib_fk_1": map[string]any{
						"local":   "a",
						"foreign": "b",
					},
				},
			},
		},
	}

	aliases := ConvertAliases(intf)

	if len(aliases.Tables) != 1 {
		t.Fatalf("should have one table alias: %#v", aliases.Tables)
	}

	table := aliases.Tables["table_name"]
	if table.UpPlural != "a" {
		t.Error("value was wrong:", table.UpPlural)
	}
	if table.UpSingular != "b" {
		t.Error("value was wrong:", table.UpSingular)
	}
	if table.DownPlural != "c" {
		t.Error("value was wrong:", table.DownPlural)
	}
	if table.DownSingular != "d" {
		t.Error("value was wrong:", table.DownSingular)
	}

	if len(table.Columns) != 1 {
		t.Error("should have one column")
	}

	if table.Columns["a"] != "b" {
		t.Error("column alias was wrong")
	}

	if len(aliases.Tables) != 1 {
		t.Fatal("should have one relationship alias")
	}

	rel := table.Relationships["ib_fk_1"]
	if rel.Local != "a" {
		t.Error("value was wrong:", rel.Local)
	}
	if rel.Foreign != "b" {
		t.Error("value was wrong:", rel.Foreign)
	}
}

func TestConvertAliasesAltSyntax(t *testing.T) {
	t.Parallel()

	var intf any = map[string]any{
		"tables": []any{
			map[string]any{
				"name":          "table_name",
				"up_plural":     "a",
				"up_singular":   "b",
				"down_plural":   "c",
				"down_singular": "d",

				"columns": []any{
					map[string]any{
						"name":  "a",
						"alias": "b",
					},
				},
				"relationships": []any{
					map[string]any{
						"name":    "ib_fk_1",
						"local":   "a",
						"foreign": "b",
					},
				},
			},
		},
	}

	aliases := ConvertAliases(intf)

	if len(aliases.Tables) != 1 {
		t.Fatalf("should have one table alias: %#v", aliases.Tables)
	}

	table := aliases.Tables["table_name"]
	if table.UpPlural != "a" {
		t.Error("value was wrong:", table.UpPlural)
	}
	if table.UpSingular != "b" {
		t.Error("value was wrong:", table.UpSingular)
	}
	if table.DownPlural != "c" {
		t.Error("value was wrong:", table.DownPlural)
	}
	if table.DownSingular != "d" {
		t.Error("value was wrong:", table.DownSingular)
	}

	if len(table.Columns) != 1 {
		t.Error("should have one column")
	}

	if table.Columns["a"] != "b" {
		t.Error("column alias was wrong")
	}

	if len(aliases.Tables) != 1 {
		t.Fatal("should have one relationship alias")
	}

	rel := table.Relationships["ib_fk_1"]
	if rel.Local != "a" {
		t.Error("value was wrong:", rel.Local)
	}
	if rel.Foreign != "b" {
		t.Error("value was wrong:", rel.Foreign)
	}
}

func TestConvertTypeReplace(t *testing.T) {
	t.Parallel()

	fullColumn := map[string]any{
		"name":           "a",
		"type":           "b",
		"db_type":        "c",
		"udt_name":       "d",
		"full_db_type":   "e",
		"arr_type":       "f",
		"tables":         []string{"g", "h"},
		"auto_generated": true,
		"nullable":       true,
	}

	var intf any = []any{
		map[string]any{
			"match":   fullColumn,
			"replace": fullColumn,
			"imports": map[string]any{
				"standard": []any{
					"abc",
				},
				"third_party": []any{
					"github.com/abc",
				},
			},
		},
	}

	typeReplace := ConvertTypeReplace(intf)
	if len(typeReplace) != 1 {
		t.Error("should have one entry")
	}

	checkColumn := func(t *testing.T, c drivers.Column) {
		t.Helper()
		if c.Name != "a" {
			t.Error("value was wrong:", c.Name)
		}
		if c.Type != "b" {
			t.Error("value was wrong:", c.Type)
		}
		if c.DBType != "c" {
			t.Error("value was wrong:", c.DBType)
		}
		if c.UDTName != "d" {
			t.Error("value was wrong:", c.UDTName)
		}
		if c.FullDBType != "e" {
			t.Error("value was wrong:", c.FullDBType)
		}
		if *c.ArrType != "f" {
			t.Error("value was wrong:", c.ArrType)
		}
		if c.AutoGenerated != true {
			t.Error("value was wrong:", c.AutoGenerated)
		}
		if c.Nullable != true {
			t.Error("value was wrong:", c.Nullable)
		}
	}

	r := typeReplace[0]
	checkColumn(t, r.Match)
	checkColumn(t, r.Replace)

	if got := r.Imports.Standard[0]; got != "abc" {
		t.Error("standard import wrong:", got)
	}
	if got := r.Imports.ThirdParty[0]; got != "github.com/abc" {
		t.Error("standard import wrong:", got)
	}
	if got := r.Tables; !reflect.DeepEqual(r.Tables, []string{"g", "h"}) {
		t.Error("tables in types.match wrong:", got)
	}
}

func TestConvertForeignKeys(t *testing.T) {
	t.Parallel()

	var intf any = map[string]any{
		"fk_1": map[string]any{
			"table":          "table_name",
			"column":         "column_name",
			"foreign_table":  "foreign_table_name",
			"foreign_column": "foreign_column_name",
		},
	}

	fks := ConvertForeignKeys(intf)
	if len(fks) != 1 {
		t.Error("should have one entry")
	}

	fk := fks[0]
	expectedFK := drivers.ForeignKey{
		Name:          "fk_1",
		Table:         "table_name",
		Column:        "column_name",
		ForeignTable:  "foreign_table_name",
		ForeignColumn: "foreign_column_name",
	}

	if fk != expectedFK {
		t.Error("value was wrong:", fk)
	}
}

func TestConvertForeignKeysAltSyntax(t *testing.T) {
	t.Parallel()

	var intf any = []any{
		map[string]any{
			"name":           "fk_1",
			"table":          "table_name",
			"column":         "column_name",
			"foreign_table":  "foreign_table_name",
			"foreign_column": "foreign_column_name",
		},
	}

	fks := ConvertForeignKeys(intf)
	if len(fks) != 1 {
		t.Error("should have one entry")
	}

	fk := fks[0]
	expectedFK := drivers.ForeignKey{
		Name:          "fk_1",
		Table:         "table_name",
		Column:        "column_name",
		ForeignTable:  "foreign_table_name",
		ForeignColumn: "foreign_column_name",
	}

	if fk != expectedFK {
		t.Error("value was wrong:", fk)
	}
}