package gemtext_test

import (
	"testing"

	"tildegit.org/tjp/gus/gemini/gemtext"
)

func TestParseLinkLine(t *testing.T) {
	tests := []struct {
		input string
		url   string
		label string
	}{
		{
			input: "=> gemini.ctrl-c.club/~tjp/ home page\r\n",
			url:   "gemini.ctrl-c.club/~tjp/",
			label: "home page",
		},
		{
			input: "=> gemi.dev/\n",
			url:   "gemi.dev/",
		},
		{
			input: "=> /gemlog/foobar 2023-01-13 - Foo Bar\n",
			url:   "/gemlog/foobar",
			label: "2023-01-13 - Foo Bar",
		},
	}

	for _, test := range tests {
		t.Run(test.input, func(t *testing.T) {
			line := gemtext.ParseLine([]byte(test.input))
			if line == nil {
				t.Fatal("ParseLine() returned nil line")
			}
			if string(line.Raw()) != string(test.input) {
				t.Error("Raw() does not match input")
			}

			if line.Type() != gemtext.LineTypeLink {
				t.Errorf("expected LineTypeLink, got %d", line.Type())
			}
			link, ok := line.(gemtext.LinkLine)
			if !ok {
				t.Fatalf("expected a LinkLine, got %T", line)
			}

			if link.URL() != test.url {
				t.Errorf("expected url %q, got %q", test.url, link.URL())
			}

			if link.Label() != test.label {
				t.Errorf("expected label %q, got %q", test.label, link.Label())
			}
		})
	}
}

func TestParsePreformatToggleLine(t *testing.T) {
	tests := []struct {
		input   string
		altText string
	}{
		{
			input: "```\n",
		},
		{
			input:   "```some alt-text\r\n",
			altText: "some alt-text",
		},
		{
			input:   "``` leading space preserved\n",
			altText: " leading space preserved",
		},
	}

	for _, test := range tests {
		t.Run(test.input, func(t *testing.T) {
			line := gemtext.ParseLine([]byte(test.input))
			if line == nil {
				t.Fatal("ParseLine() returned nil line")
			}
			if string(line.Raw()) != string(test.input) {
				t.Error("Raw() does not match input")
			}

			if line.Type() != gemtext.LineTypePreformatToggle {
				t.Errorf("expected LineTypePreformatToggle, got %d", line.Type())
			}
			toggle, ok := line.(gemtext.PreformatToggleLine)
			if !ok {
				t.Fatalf("expected a PreformatToggleLine, got %T", line)
			}

			if toggle.AltText() != test.altText {
				t.Errorf("expected alt-text %q, got %q", test.altText, toggle.AltText())
			}
		})
	}
}

func TestParseHeadingLine(t *testing.T) {
	tests := []struct {
		input    string
		lineType gemtext.LineType
		body     string
	}{
		{
			input:    "# this is an H1\n",
			lineType: gemtext.LineTypeHeading1,
			body:     "this is an H1",
		},
		{
			input:    "##    extra leading spaces\r\n",
			lineType: gemtext.LineTypeHeading2,
			body:     "extra leading spaces",
		},
		{
			input: "##no leading space\n",
			lineType: gemtext.LineTypeHeading2,
			body: "no leading space",
		},
		{
			input:    "#### there is no h4\n",
			lineType: gemtext.LineTypeHeading3,
			body:     "# there is no h4",
		},
	}

	for _, test := range tests {
		t.Run(test.input, func(t *testing.T) {
			line := gemtext.ParseLine([]byte(test.input))
			if line == nil {
				t.Fatal("ParseLine() returned nil")
			}

			if line.Type() != test.lineType {
				t.Errorf("expected line type %d, got %d", test.lineType, line.Type())
			}
			if string(line.Raw()) != test.input {
				t.Error("line.Raw() does not match input")
			}

			hdg, ok := line.(gemtext.HeadingLine)
			if !ok {
				t.Fatalf("expected HeadingLine, got a %T", line)
			}

			if hdg.Body() != test.body {
				t.Errorf("expected body %q, got %q", test.body, hdg.Body())
			}
		})
	}
}

func TestParseListItemLine(t *testing.T) {
	tests := []struct {
		input string
		body  string
	}{
		{
			input: "* this is a list item\r\n",
			body:  "this is a list item",
		},
		{
			input: "*   more leading spaces\n",
			body:  "  more leading spaces",
		},
	}

	for _, test := range tests {
		t.Run(test.input, func(t *testing.T) {
			line := gemtext.ParseLine([]byte(test.input))
			if line == nil {
				t.Fatal("ParseLine() returned nil")
			}

			if line.Type() != gemtext.LineTypeListItem {
				t.Errorf("expected LineTypeListItem, got %d", line.Type())
			}
			if string(line.Raw()) != test.input {
				t.Error("line.Raw() does not match input")
			}

			li, ok := line.(gemtext.ListItemLine)
			if !ok {
				t.Fatalf("expected ListItemLine, got a %T", line)
			}

			if li.Body() != test.body {
				t.Errorf("expected body %q, got %q", test.body, li.Body())
			}
		})
	}
}

func TestParseQuoteLine(t *testing.T) {
	tests := []struct {
		input string
		body  string
	}{
		{
			input: ">a quote line\r\n",
			body:  "a quote line",
		},
		{
			input: "> with a leading space\n",
			body:  " with a leading space",
		},
		{
			input: ">   more leading spaces\n",
			body:  "   more leading spaces",
		},
	}

	for _, test := range tests {
		t.Run(test.input, func(t *testing.T) {
			line := gemtext.ParseLine([]byte(test.input))
			if line == nil {
				t.Fatal("ParseLine() returned nil")
			}

			if line.Type() != gemtext.LineTypeQuote {
				t.Errorf("expected LineTypeQuote, got %d", line.Type())
			}
			if string(line.Raw()) != test.input {
				t.Error("line.Raw() does not match input")
			}

			qu, ok := line.(gemtext.QuoteLine)
			if !ok {
				t.Fatalf("expected QuoteLine , got a %T", line)
			}

			if qu.Body() != test.body {
				t.Errorf("expected body %q, got %q", test.body, qu.Body())
			}
		})
	}
}

func TestParseTextLine(t *testing.T) {
	tests := []string {
		"\n",
		"simple text line\r\n",
		" * an invalid list item\n",
		"*another invalid list item\r\n",
	}

	for _, test := range tests {
		t.Run(test, func(t *testing.T) {
			line := gemtext.ParseLine([]byte(test))
			if line == nil {
				t.Fatal("ParseLine() returned nil")
			}

			if line.Type() != gemtext.LineTypeText {
				t.Errorf("expected LineTypeText, got %d", line.Type())
			}
			if string(line.Raw()) != test {
				t.Error("line.Raw() does not match input")
			}

			_, ok := line.(gemtext.TextLine)
			if !ok {
				t.Fatalf("expected TextLine , got a %T", line)
			}
		})
	}
}