package parser_test

import (
	"backend/parser"
	"backend/utils"
	"fmt"
	"testing"
	"time"
)

func TestParseDettaglio(t *testing.T) {
	// Helper to build expected date string
	buildExpectedDate := func(day int, month int) string {
		year := time.Now().Year()
		currMonth := int(time.Now().Month())
		if month < currMonth {
			year++
		}
		return fmt.Sprintf("%d-%02d-%02d", year, month, day)
	}

	consegnaOct := utils.Ptr(10) // October
	consegnaFeb := utils.Ptr(2)  // February

	tests := []struct {
		name         string
		input        string
		currConsegna *int
		wantDisp     *string
		wantPerm     *[]int
		wantPers     *[]string
	}{
		{
			name:         "all fields present",
			input:        "disp:01/10 perm:3-6 persone:single",
			currConsegna: consegnaOct,
			wantDisp:     utils.Ptr(buildExpectedDate(1, 10)),
			wantPerm:     utils.Ptr([]int{3, 6}),
			wantPers:     utils.Ptr([]string{"single"}),
		},
		{
			name:         "all fields empty",
			input:        "disp: perm: persone:",
			currConsegna: consegnaOct,
			wantDisp:     utils.Ptr(buildExpectedDate(1, 10)), // defaults to 1st
			wantPerm:     nil,
			wantPers:     nil,
		},
		{
			name:         "disp missing value",
			input:        "disp: perm:3-6 persone:single",
			currConsegna: consegnaOct,
			wantDisp:     utils.Ptr(buildExpectedDate(1, 10)), // defaults to 1st
			wantPerm:     utils.Ptr([]int{3, 6}),
			wantPers:     utils.Ptr([]string{"single"}),
		},
		{
			name:         "perm missing",
			input:        "disp:01/10 perm: persone:single",
			currConsegna: consegnaOct,
			wantDisp:     utils.Ptr(buildExpectedDate(1, 10)),
			wantPerm:     nil,
			wantPers:     utils.Ptr([]string{"single"}),
		},
		{
			name:         "persone missing",
			input:        "disp:01/10 perm:3-6 persone:",
			currConsegna: consegnaOct,
			wantDisp:     utils.Ptr(buildExpectedDate(1, 10)),
			wantPerm:     utils.Ptr([]int{3, 6}),
			wantPers:     nil,
		},
		{
			name:         "invalid date - defaults to 1st",
			input:        "disp:bad_date perm:3-6 persone:single",
			currConsegna: consegnaOct,
			wantDisp:     utils.Ptr(buildExpectedDate(1, 10)), // defaults to 1st
			wantPerm:     utils.Ptr([]int{3, 6}),
			wantPers:     utils.Ptr([]string{"single"}),
		},
		{
			name:         "non-numeric perm",
			input:        "disp:01/10 perm:abc-6 persone:single",
			currConsegna: consegnaOct,
			wantDisp:     utils.Ptr(buildExpectedDate(1, 10)),
			wantPerm:     utils.Ptr([]int{6}),
			wantPers:     utils.Ptr([]string{"single"}),
		},
		{
			name:         "trailing delimiter in perm",
			input:        "disp:01/10 perm:3- persone:single",
			currConsegna: consegnaOct,
			wantDisp:     utils.Ptr(buildExpectedDate(1, 10)),
			wantPerm:     utils.Ptr([]int{3}),
			wantPers:     utils.Ptr([]string{"single"}),
		},
		{
			name:         "leading delimiter in perm",
			input:        "disp:01/10 perm:-6 persone:single",
			currConsegna: consegnaOct,
			wantDisp:     utils.Ptr(buildExpectedDate(1, 10)),
			wantPerm:     utils.Ptr([]int{6}),
			wantPers:     utils.Ptr([]string{"single"}),
		},
		{
			name:         "trailing delimiter in persone",
			input:        "disp:01/10 perm:3-6 persone:single/",
			currConsegna: consegnaOct,
			wantDisp:     utils.Ptr(buildExpectedDate(1, 10)),
			wantPerm:     utils.Ptr([]int{3, 6}),
			wantPers:     utils.Ptr([]string{"single"}),
		},
		{
			name:         "leading delimiter in persone",
			input:        "disp:01/10 perm:3-6 persone:/single",
			currConsegna: consegnaOct,
			wantDisp:     utils.Ptr(buildExpectedDate(1, 10)),
			wantPerm:     utils.Ptr([]int{3, 6}),
			wantPers:     utils.Ptr([]string{"single"}),
		},
		{
			name:         "double delimiter in persone",
			input:        "disp:01/10 perm:3-6 persone:single//coppia",
			currConsegna: consegnaOct,
			wantDisp:     utils.Ptr(buildExpectedDate(1, 10)),
			wantPerm:     utils.Ptr([]int{3, 6}),
			wantPers:     utils.Ptr([]string{"single", "coppia"}),
		},
		{
			name:         "no disp field",
			input:        "perm:3-6 persone:single",
			currConsegna: consegnaOct,
			wantDisp:     utils.Ptr(buildExpectedDate(1, 10)),
			wantPerm:     utils.Ptr([]int{3, 6}),
			wantPers:     utils.Ptr([]string{"single"}),
		},
		{
			name:         "no persone field",
			input:        "disp:01/10 perm:3-6",
			currConsegna: consegnaOct,
			wantDisp:     utils.Ptr(buildExpectedDate(1, 10)),
			wantPerm:     utils.Ptr([]int{3, 6}),
			wantPers:     nil,
		},
		{
			name:         "empty string",
			input:        "",
			currConsegna: consegnaOct,
			wantDisp:     utils.Ptr(buildExpectedDate(1, 10)),
			wantPerm:     nil,
			wantPers:     nil,
		},
		{
			name:         "whitespace in values",
			input:        "disp: 01/10   perm:  3-6   persone: single ",
			currConsegna: consegnaOct,
			wantDisp:     utils.Ptr(buildExpectedDate(1, 10)),
			wantPerm:     utils.Ptr([]int{3, 6}),
			wantPers:     utils.Ptr([]string{"single"}),
		},
		{
			name:         "different order",
			input:        "persone:single perm:3-6 disp:01/10",
			currConsegna: consegnaOct,
			wantDisp:     utils.Ptr(buildExpectedDate(1, 10)),
			wantPerm:     utils.Ptr([]int{3, 6}),
			wantPers:     utils.Ptr([]string{"single"}),
		},
		{
			name:         "double dash in perm",
			input:        "disp:01/10 perm:3--6 persone:single",
			currConsegna: consegnaOct,
			wantDisp:     utils.Ptr(buildExpectedDate(1, 10)),
			wantPerm:     utils.Ptr([]int{3, 6}),
			wantPers:     utils.Ptr([]string{"single"}),
		},
		{
			name:         "tab separators",
			input:        "disp:01/10\tperm:3-6\tpersone:single",
			currConsegna: consegnaOct,
			wantDisp:     utils.Ptr(buildExpectedDate(1, 10)),
			wantPerm:     utils.Ptr([]int{3, 6}),
			wantPers:     utils.Ptr([]string{"single"}),
		},
		{
			name:         "special chars in persone",
			input:        "disp:01/10 perm:3-6 persone:single/coppia!",
			currConsegna: consegnaOct,
			wantDisp:     utils.Ptr(buildExpectedDate(1, 10)),
			wantPerm:     utils.Ptr([]int{3, 6}),
			wantPers:     utils.Ptr([]string{"single", "coppia!"}),
		},
		{
			name:         "duplicate disp - first wins",
			input:        "disp:01/10 disp:02/11 perm:3-6 persone:single",
			currConsegna: consegnaOct,
			wantDisp:     utils.Ptr(buildExpectedDate(1, 10)),
			wantPerm:     utils.Ptr([]int{3, 6}),
			wantPers:     utils.Ptr([]string{"single"}),
		},
		{
			name:         "no spaces between fields",
			input:        "disp:01/10perm:3-6persone:single",
			currConsegna: consegnaOct,
			wantDisp:     utils.Ptr(buildExpectedDate(1, 10)),
			wantPerm:     utils.Ptr([]int{3, 6}),
			wantPers:     utils.Ptr([]string{"single"}),
		},
		{
			name:         "day only in disp",
			input:        "disp:15 perm:3-6 persone:single",
			currConsegna: consegnaOct,
			wantDisp:     utils.Ptr(buildExpectedDate(15, 10)),
			wantPerm:     utils.Ptr([]int{3, 6}),
			wantPers:     utils.Ptr([]string{"single"}),
		},
		{
			name:         "invalid day 31 for February",
			input:        "disp:31 perm:3-6 persone:single",
			currConsegna: consegnaFeb,
			wantDisp:     utils.Ptr(buildExpectedDate(1, 2)),
			wantPerm:     utils.Ptr([]int{3, 6}),
			wantPers:     utils.Ptr([]string{"single"}),
		},
		{
			name:         "nil currConsegna",
			input:        "disp:01/10 perm:3-6 persone:single",
			currConsegna: nil,
			wantDisp:     nil,
			wantPerm:     utils.Ptr([]int{3, 6}),
			wantPers:     utils.Ptr([]string{"single"}),
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			disp_time, perm_arr, persone_arr, _ := parser.ParseDettaglio(tt.input, tt.currConsegna)

			// Handle *string comparison, including nil
			if tt.wantDisp != nil {
				if disp_time == nil {
					t.Errorf("disp_time: got nil, want %v", *tt.wantDisp)
				} else if *disp_time != *tt.wantDisp {
					t.Errorf("disp_time: got %v, want %v", *disp_time, *tt.wantDisp)
				}
			} else if disp_time != nil {
				t.Errorf("disp_time: got %v, want nil", *disp_time)
			}

			// Handle *[]int comparison, including nil
			if tt.wantPerm != nil {
				if perm_arr == nil || len(*perm_arr) != len(*tt.wantPerm) {
					t.Errorf("perm_arr: got %v, want %v", perm_arr, *tt.wantPerm)
				} else {
					for i := range *perm_arr {
						if (*perm_arr)[i] != (*tt.wantPerm)[i] {
							t.Errorf("perm_arr[%d]: got %v, want %v", i, (*perm_arr)[i], (*tt.wantPerm)[i])
						}
					}
				}
			} else if perm_arr != nil && len(*perm_arr) != 0 {
				t.Errorf("perm_arr: got %v, want nil", perm_arr)
			}

			// Handle *[]string comparison, including nil
			if tt.wantPers != nil {
				if persone_arr == nil || len(*persone_arr) != len(*tt.wantPers) {
					t.Errorf("persone_arr: got %v, want %v", persone_arr, *tt.wantPers)
				} else {
					for i := range *persone_arr {
						if (*persone_arr)[i] != (*tt.wantPers)[i] {
							t.Errorf("persone_arr[%d]: got %v, want %v", i, (*persone_arr)[i], (*tt.wantPers)[i])
						}
					}
				}
			} else if persone_arr != nil && len(*persone_arr) != 0 {
				t.Errorf("persone_arr: got %v, want nil", persone_arr)
			}
		})
	}
}

func TestParseConsegna(t *testing.T) {
	tests := []struct {
		input        *string
		wantConsegna *int
	}{
		// Test all Italian month names
		{
			input:        utils.Ptr("Gennaio"),
			wantConsegna: utils.Ptr(1),
		},
		{
			input:        utils.Ptr("Febbraio"),
			wantConsegna: utils.Ptr(2),
		},
		{
			input:        utils.Ptr("Marzo"),
			wantConsegna: utils.Ptr(3),
		},
		{
			input:        utils.Ptr("Aprile"),
			wantConsegna: utils.Ptr(4),
		},
		{
			input:        utils.Ptr("Maggio"),
			wantConsegna: utils.Ptr(5),
		},
		{
			input:        utils.Ptr("Giugno"),
			wantConsegna: utils.Ptr(6),
		},
		{
			input:        utils.Ptr("Luglio"),
			wantConsegna: utils.Ptr(7),
		},
		{
			input:        utils.Ptr("Agosto"),
			wantConsegna: utils.Ptr(8),
		},
		{
			input:        utils.Ptr("Settembre"),
			wantConsegna: utils.Ptr(9),
		},
		{
			input:        utils.Ptr("Ottobre"),
			wantConsegna: utils.Ptr(10),
		},
		{
			input:        utils.Ptr("Novembre"),
			wantConsegna: utils.Ptr(11),
		},
		{
			input:        utils.Ptr("Dicembre"),
			wantConsegna: utils.Ptr(12),
		},
		// Test "Libero" (should return current month)
		{
			input:        utils.Ptr("Libero"),
			wantConsegna: utils.Ptr(int(time.Now().Month())),
		},
		// Test nil input
		{
			input:        nil,
			wantConsegna: nil,
		},
		// Test empty string
		{
			input:        utils.Ptr(""),
			wantConsegna: utils.Ptr(int(time.Now().Month())),
		},
		// Test lowercase (should return current month as default)
		{
			input:        utils.Ptr("gennaio"),
			wantConsegna: utils.Ptr(int(time.Now().Month())),
		},
		// Test with spaces
		{
			input:        utils.Ptr("Gennaio "),
			wantConsegna: utils.Ptr(int(time.Now().Month())),
		},
		{
			input:        utils.Ptr(" Gennaio"),
			wantConsegna: utils.Ptr(int(time.Now().Month())),
		},
		// Test with special characters
		{
			input:        utils.Ptr("Gennaio!"),
			wantConsegna: utils.Ptr(int(time.Now().Month())),
		},
		// Test abbreviated forms
		{
			input:        utils.Ptr("Gen"),
			wantConsegna: utils.Ptr(int(time.Now().Month())),
		},
		{
			input:        utils.Ptr("Feb"),
			wantConsegna: utils.Ptr(int(time.Now().Month())),
		},
		// Test English month names
		{
			input:        utils.Ptr("January"),
			wantConsegna: utils.Ptr(int(time.Now().Month())),
		},
		// Test numeric strings
		{
			input:        utils.Ptr("1"),
			wantConsegna: utils.Ptr(int(time.Now().Month())),
		},
		// Test mixed cases
		{
			input:        utils.Ptr("GEnNaIo"),
			wantConsegna: utils.Ptr(int(time.Now().Month())),
		},
		// Test completely unrelated strings
		{
			input:        utils.Ptr("NotAMonth"),
			wantConsegna: utils.Ptr(int(time.Now().Month())),
		},
	}

	for _, tt := range tests {
		gotConsegna := parser.ParseConsegna(tt.input)

		if tt.wantConsegna != nil {
			if gotConsegna == nil {
				t.Errorf("got nil, want %v, input: %v", *tt.wantConsegna, tt.input)
			} else if *gotConsegna != *tt.wantConsegna {
				t.Errorf("got %v, want %v, input: %v", *gotConsegna, *tt.wantConsegna, tt.input)
			}
		} else if gotConsegna != nil {
			t.Errorf("got %v, want nil, input: %v", *gotConsegna, tt.input)
		}
	}
}

func TestParsePrezzo(t *testing.T) {
	tests := []struct {
		input *string
		want  int
	}{
		{input: utils.Ptr("100"), want: 10000},
		{input: utils.Ptr("€ 100"), want: 10000},
		{input: utils.Ptr("100€"), want: 10000},
		{input: utils.Ptr("€100"), want: 10000},
		{input: utils.Ptr("100 €"), want: 10000},
		{input: utils.Ptr("€ 100,50"), want: 10050},
		{input: utils.Ptr("100,50€"), want: 10050},
		{input: utils.Ptr("€100,50"), want: 10050},
		{input: utils.Ptr("100,50 €"), want: 10050},
		{input: utils.Ptr("€ 1.000"), want: 100000},
		{input: utils.Ptr("1.000€"), want: 100000},
		{input: utils.Ptr("€1.000"), want: 100000},
		{input: utils.Ptr("1.000 €"), want: 100000},
		{input: utils.Ptr("€ 1.000,50"), want: 100050},
		{input: utils.Ptr("1.000,50€"), want: 100050},
		{input: utils.Ptr("€1.000,50"), want: 100050},
		{input: utils.Ptr("1.000,50 €"), want: 100050},
	}

	for _, tt := range tests {
		got := parser.ParsePrezzo(tt.input)

		if got != tt.want {
			inputStr := ""
			if tt.input != nil {
				inputStr = *tt.input
			}
			t.Errorf("got %v, want %v, input: %v", got, tt.want, inputStr)
		}
	}
}

func TestProcessTipologia(t *testing.T) {
	tests := []struct {
		t1, t2 *string
		want   *string
	}{
		{t1: utils.Ptr("V"), want: utils.Ptr("Vendita")},
		{t1: utils.Ptr("I"), want: utils.Ptr("Cessione")},
		{t1: utils.Ptr("A"), t2: utils.Ptr("Residenziale"), want: utils.Ptr("Transitorio")},
		{t1: utils.Ptr("A"), t2: utils.Ptr("Turistico"), want: utils.Ptr("Transitorio")},
		{t1: utils.Ptr("A"), t2: utils.Ptr("Stanza"), want: utils.Ptr("Transitorio")},
		{t1: utils.Ptr("A"), t2: nil, want: utils.Ptr("Stabile")},
		{t1: nil, want: utils.Ptr("Errore")},
		// Unknown t1 value
		{t1: utils.Ptr("X"), want: utils.Ptr("Errore")},
		// Lowercase t1 value
		{t1: utils.Ptr("v"), want: utils.Ptr("Errore")},
		// t1 is empty string
		{t1: utils.Ptr(""), want: utils.Ptr("Errore")},
		// t2 is empty string with t1 == "A"
		{t1: utils.Ptr("A"), t2: utils.Ptr(""), want: utils.Ptr("Stabile")},
		// t2 is unknown value with t1 == "A"
		{t1: utils.Ptr("A"), t2: utils.Ptr("Commerciale"), want: utils.Ptr("Stabile")},
		// t2 is lowercase valid value with t1 == "A"
		{t1: utils.Ptr("A"), t2: utils.Ptr("residenziale"), want: utils.Ptr("Transitorio")},
		// t2 is uppercase valid value with t1 == "A"
		{t1: utils.Ptr("A"), t2: utils.Ptr("RESIDENZIALE"), want: utils.Ptr("Transitorio")},
		// t2 is "Stanza" with extra spaces
		{t1: utils.Ptr("A"), t2: utils.Ptr(" Stanza "), want: utils.Ptr("Transitorio")},
	}

	for _, tt := range tests {
		got := parser.ParseTipologia(tt.t1, tt.t2)

		if got == nil || *got != *tt.want {
			t.Errorf("got %v, want %v, input t1: %v, t2: %v", *got, *tt.want, *tt.t1, *tt.t2)
		}
	}
}

func TestParseEmail(t *testing.T) {
	tests := []struct {
		input *string
		want  *string
	}{
		{input: utils.Ptr(" test@test.it "), want: utils.Ptr("test@test.it")},
		{input: utils.Ptr("user@example.com"), want: utils.Ptr("user@example.com")},
		{input: utils.Ptr("admin@domain.org"), want: utils.Ptr("admin@domain.org")},
		{input: utils.Ptr("contact@company.co.uk"), want: utils.Ptr("contact@company.co.uk")},
		{input: utils.Ptr("user.name@example.com"), want: utils.Ptr("user.name@example.com")},
		{input: utils.Ptr("user+tag@example.com"), want: utils.Ptr("user+tag@example.com")},
		{input: utils.Ptr("123@example.com"), want: utils.Ptr("123@example.com")},

		// Emails with extra whitespace
		{input: utils.Ptr("  email@test.com  "), want: utils.Ptr("email@test.com")},
		{input: utils.Ptr("\temail@test.com\t"), want: utils.Ptr("email@test.com")},
		{input: utils.Ptr("\nemail@test.com\n"), want: utils.Ptr("email@test.com")},

		// Invalid emails (should return nil)
		{input: utils.Ptr("invalid-email"), want: nil},
		{input: utils.Ptr("@domain.com"), want: nil},
		{input: utils.Ptr("user@"), want: nil},
		{input: utils.Ptr("user@domain"), want: nil},
		{input: utils.Ptr("user.domain.com"), want: nil},
		{input: utils.Ptr("user@@domain.com"), want: nil},
		{input: utils.Ptr(""), want: nil},
		{input: utils.Ptr("   "), want: nil},
		{input: nil, want: nil},

		// Edge cases
		{input: utils.Ptr("a@b.co"), want: utils.Ptr("a@b.co")},
		{input: utils.Ptr("test@test-domain.com"), want: utils.Ptr("test@test-domain.com")},
		{input: utils.Ptr("user@sub.domain.com"), want: utils.Ptr("user@sub.domain.com")},
		{input: utils.Ptr("lui.stra2006@gmail.com - LUIGINA"), want: utils.Ptr("lui.stra2006@gmail.com")},
	}
	for _, tt := range tests {
		got := parser.ParseEmail(tt.input)
		if tt.want == nil {
			if got != nil {

				inputStr := "nil"
				if tt.input != nil {
					inputStr = *tt.input
				}
				t.Errorf("got %q, want nil, input: %q", *got, inputStr)
			}
		} else {
			if got == nil {
				inputStr := "nil"
				if tt.input != nil {
					inputStr = *tt.input
				}
				t.Errorf("got nil, want %q, input: %q", *tt.want, inputStr)
			} else if *got != *tt.want {
				inputStr := "nil"
				if tt.input != nil {
					inputStr = *tt.input
				}
				t.Errorf("got %q, want %q, input: %q", *got, *tt.want, inputStr)
			}
		}
	}
}
