package e2etests

import (
	"testing"
)

func testRegression(t *testing.T) {
	tcs := []testCase{
		{
			// https://github.com/terrastruct/d2/issues/919
			name: "hex-fill",
			script: `x: {
  style.fill: "#0D32B2"
}
`,
		},
		{
			name: "dagre_special_ids",
			script: `
ninety\nnine
eighty\reight
seventy\r\nseven
a\\yode -> there
a\\"ode -> there
a\\node -> there
`,
		},
		{
			name: "empty_sequence",
			script: `
A: hello {
  shape: sequence_diagram
}

B: goodbye {
  shape: sequence_diagram
}

A->B`,
		},
		{
			name: "undeclared_nested_sequence",
			script: `shape: sequence_diagram
group.nested: {
  a -> b
}
`,
			expErr: "no actors declared in sequence diagram",
		},
		{
			name: "class_font_style_sequence",
			script: `shape: sequence_diagram
a: {
  shape: class
  style: {
    font-color: red
  }
}
`,
		},
		{
			name: "nested_steps",
			script: `a: {
  a: {
    shape: step
  }
  b: {
    shape: step
  }
  a -> b
}

c: {
  shape: step
}
d: {
  shape: step
}
c -> d
`,
		},
		{
			name: "sequence_diagram_span_cover",
			script: `shape: sequence_diagram
b.1 -> b.1
b.1 -> b.1`,
		}, {
			name: "sequence_diagram_no_message",
			script: `shape: sequence_diagram
a: A
b: B`,
		},
		{
			name: "root-container",
			script: `main: {
  x -> y
  y <- z
}

root: {
  x -> y
  y <- z
}`,
		},
		{
			name: "sequence_diagram_name_crash",
			script: `foo: {
	shape: sequence_diagram
	a -> b
}
foobar: {
	shape: sequence_diagram
	c -> d
}
foo -> foobar`,
		},
		{
			name: "sql_table_overflow",
			script: `
table: sql_table_overflow {
	shape: sql_table
	short: loooooooooooooooooooong
	loooooooooooooooooooong: short
}
table_constrained: sql_table_constrained_overflow {
	shape: sql_table
	short: loooooooooooooooooooong {
		constraint: unique
	}
	loooooooooooooooooooong: short {
		constraint: foreign_key
	}
}
`,
		},
		{
			name: "elk_alignment",
			script: `
direction: down

build_workflow: lambda-build.yaml {

	push: Push to main branch {
		style.font-size: 25
	}

	GHA: GitHub Actions {
		style.font-size: 25
	}

	S3.style.font-size: 25
	Terraform.style.font-size: 25
	AWS.style.font-size: 25

	push -> GHA: Triggers {
		style.font-size: 20
	}

	GHA -> S3: Builds zip and pushes it {
		style.font-size: 20
	}

	S3 <-> Terraform: Pulls zip to deploy {
		style.font-size: 20
	}

	Terraform -> AWS: Changes live lambdas {
		style.font-size: 20
	}
}

deploy_workflow: lambda-deploy.yaml {

	manual: Manual Trigger {
		style.font-size: 25
	}

	GHA: GitHub Actions {
		style.font-size: 25
	}

	AWS.style.font-size: 25

	Manual -> GHA: Launches {
		style.font-size: 20
	}

	GHA -> AWS: Builds zip\npushes them to S3.\n\nDeploys lambdas\nusing Terraform {
		style.font-size: 20
	}
}

apollo_workflow: apollo-deploy.yaml {

	apollo: Apollo Repo {
		style.font-size: 25
	}

	GHA: GitHub Actions {
		style.font-size: 25
	}

	AWS.style.font-size: 25

	apollo -> GHA: Triggered manually/push to master test test test test test test test {
		style.font-size: 20
	}

	GHA -> AWS: test {
		style.font-size: 20
	}
}
`,
		},
		{
			name: "dagre_edge_label_spacing",
			script: `direction: right

build_workflow: lambda-build.yaml {

	push: Push to main branch {
		style.font-size: 25
	}
	GHA: GitHub Actions {
		style.font-size: 25
	}
	S3.style.font-size: 25
	Terraform.style.font-size: 25
	AWS.style.font-size: 25

	push -> GHA: Triggers
	GHA -> S3: Builds zip & pushes it
	S3 <-> Terraform: Pulls zip to deploy
	Terraform -> AWS: Changes the live lambdas
}
`,
		},
		{
			name: "query_param_escape",
			script: `my network: {
  icon: https://icons.terrastruct.com/infra/019-network.svg?fuga=1&hoge
}
`,
		},
		{
			name: "elk_order",
			script: `queue: {
  shape: queue
  label: ''

  M0
  M1
  M2
  M3
  M4
  M5
  M6
}

m0_desc: |md
  Oldest message
|
m0_desc -> queue.M0

m2_desc: |md
  Offset
|
m2_desc -> queue.M2

m5_desc: |md
  Last message
|
m5_desc -> queue.M5

m6_desc: |md
  Next message will be\
  inserted here
|
m6_desc -> queue.M6
`,
		},
		{
			name: "unnamed_class_table_code",
			script: `

class2 -> users -> code

class2: "" {
	shape: class
	-num: int
	-timeout: int
	-pid

	+getStatus(): Enum
	+getJobs(): "Job[]"
	+setTimeout(seconds int)
}

users: "" {
	shape: sql_table
	id: int
	name: string
	email: string
	password: string
	last_login: datetime
}

code: |go
    a := 5
    b := a + 7
    fmt.Printf("%d", b)
|
`,
		},
		{
			name: "elk_img_empty_label_panic",
			script: `
img: {
	label: ""
	shape: image
	icon: https://icons.terrastruct.com/infra/019-network.svg
}
ico: {
	label: ""
	icon: https://icons.terrastruct.com/infra/019-network.svg
}
`,
		},
		{
			name: "only_header_class_table",
			script: `

class2: RefreshAuthorizationPolicyProtocolServerSideTranslatorProtocolBuffer {
	shape: class
}

table: RefreshAuthorizationPolicyCache {
	shape: sql_table
}

table with short col: RefreshAuthorizationPolicyCache {
	shape: sql_table
	ok
}

class2 -> table -> table with short col
`,
		},
		{
			name: "overlapping-edge-label",
			script: `k8s: Kubernetes
k8s.m1: k8s-master1
k8s.m2: k8s-master2
k8s.m3: k8s-master3
k8s.w1: k8s-worker1
k8s.w2: k8s-worker2
k8s.w3: k8s-worker3

osvc: opensvc
osvc.vm1: VM1
osvc.vm2: VM2

k8s -> osvc: keycloak
k8s -> osvc: heptapod
k8s -> osvc: harbor
k8s -> osvc: vault
`,
		},
		{
			name: "no-lexer",
			script: `x: |d2
  x -> y
|
`,
		},
		{
			name: "dagre_broken_arrowhead",
			script: `
a.b -> a.c: "line 1\nline 2\nline 3\nline 4" {
	style: {
		font-color: red
		stroke: red
	}
	target-arrowhead: {
		shape: diamond
	}
}
a.1 -> a.c
a.2 <-> a.c
a.c {
	style.stroke: white
	d
}
`,
		},
		{
			name: "code_leading_trailing_newlines",
			script: `
hello world: |python


  # 2 leading, 2 trailing
  def hello():

    print "world"


|

no trailing: |python


  # 2 leading
  def hello():

    print "world"
|

no leading: |python
  # 2 trailing
  def hello():

    print "world"


|
`,
		},
		{
			name: "code_leading_newlines",
			script: `
5 leading: |python





  def hello():

    print "world"
|
8 total: |python
  # 1 leading
  # 2 leading
  # 3 leading
  # 4 leading
  # 5 leading
  def hello():

    print "world"
|
1 leading: |python

  def hello():

    print "world"
|
4 total: |python
  # 1 leading
  def hello():

    print "world"
|
2 leading: |python


  def hello():

    print "world"
|
5 total: |python
  # 1 leading
  # 2 leading
  def hello():

    print "world"
|
`,
		},
		{
			name: "code_trailing_newlines",
			script: `
5 trailing: |python
  def hello():

    print "world"





|
8 total: |python
  def hello():

    print "world"
  # 1 trailing
  # 2 trailing
  # 3 trailing
  # 4 trailing
  # 5 trailing
|
1 trailing: |python
  def hello():

    print "world"

|
4 total: |python
  def hello():

    print "world"
  # 1 trailing
|
2 trailing: |python
  def hello():

    print "world"


|
5 total: |python
  def hello():

    print "world"
  # 1 trailing
  # 2 trailing
|
`,
		},
		{
			name: "md_h1_li_li",
			script: mdTestScript(`
# hey
- they
	1. they
`),
		},
		{
			name: "elk_loop_panic",
			script: `x: {
  a
  b
}

x.a -> x.a
`,
		},
		{
			name: "opacity-on-label",
			script: `x.style.opacity: 0.4
y: |md
  linux: because a PC is a terrible thing to waste
| {
	style.opacity: 0.4
}
x -> a: {
  label: You don't have to know how the computer works,\njust how to work the computer.
  style.opacity: 0.4
}
`,
		},
		{
			name: "sequence_diagram_self_edge_group_overlap",
			script: `
shape: sequence_diagram
a: A
b: B
c: C
group 1: {
	a -> a
}
group 2: {
	a -> b
}
group 3: {
	a -> a.a
}
group 4: {
	a.a -> b
}
group 5: {
	b -> b
	b -> b
}
group 6: {
	b -> a
}
group 7: {
	a -> a
}
group 8: {
	a -> a
}
a -> a
group 9: {
	a -> a
}
a -> a

b -> c
group 10: {
	c -> c
}
b -> c
group 11: {
	c -> c
}
b -> c

`,
		},
		{
			name: "empty_class_height",
			script: `
class1: class with rows {
	shape: class
	-num: int
	-timeout: int
}

class2: class without rows {
	shape: class
}
`,
		},
		{
			name: "just-width",
			script: `x: "teamwork: having someone to blame" {
  width: 100
}
`,
		},
		{
			name: "sequence-panic",
			script: `
shape: sequence_diagram

a

group: {
  inner_group: {
    a -> b
  }
}
`,
			expErr: "could not find center of b. Is it declared as an actor?",
		},
		{
			name: "ampersand-escape",
			script: `hy: &∈ {
  tooltip: beans & rice
}
`,
		},
		{
			name: "dagre-disconnect",
			script: `a: {
  k.t -> f.i
  f.g -> _.s.n
}
k
k.s <-> u.o
h.m.s -> a.f.g

a.f.j -> u.s.j
u: {
  c -> _.s.z.c
}

s: {
  n: {
    style.stroke: red
    f
  }
}

s.n -> y.r: {style.stroke-width: 8; style.stroke: red}
y.r -> a.g.i: 1\n2\n3\n4
`,
		},
		{
			name: "sequence-note-escape-group",
			script: `shape: sequence_diagram
a
b

"04:20,11:20": {
  "loop through each table": {
    a."start_time = datetime.datetime.now"
    a -> b
  }
}
`,
		},
		loadFromFile(t, "unconnected"),
		{
			name: "straight_hierarchy_container_direction_right",
			script: `
direction: right
a
c
b

l1: {
	b
	a
	c
}

b -> l1.b
a -> l1.a
c -> l1.c

l2c1: {
	a
}
l1.a -> l2c1.a

l2c3: {
	c
}
l1.c -> l2c3.c

l2c2: {
	b
}
l1.b -> l2c2.b

l3c1: {
	a
	b
}
l2c1.a -> l3c1.a
l2c2.b -> l3c1.b

l3c2: {
	c
}
l2c3.c -> l3c2.c

l4: {
	c1: {
		a
	}
	c2: {
		b
	}
	c3: {
		c
	}
}
l3c1.a -> l4.c1.a
l3c1.b -> l4.c2.b
l3c2.c -> l4.c3.c`,
		},
		{
			name:   "link_with_ampersand",
			script: `a.link: https://calendar.google.com/calendar/u/0/r?tab=mc&pli=1`,
		},
		{
			name: "bold_edge_label",
			script: `
direction: right
x -> y: sync
y -> z: sync {
	style.bold: true
}
`,
		},
		{
			name: "grid_in_constant_near",
			script: `
a
b
c
x: {
	near: top-right
	grid-columns: 1
	y
	z
}
`,
		},
		{
			name: "md_font_weight",
			script: `
explanation: |md
# I can do headers

- lists
- lists

And other normal markdown stuff
|
`,
		},
		{
			name: "grid_panic",
			script: `
			2 rows 1 obj: {
				grid-rows: 2

				one
			}
			3 rows 2 obj: {
				grid-rows: 3

				one
				two
			}
			4 columns 2 obj: {
				grid-columns: 4

				one
				two
			}
`,
		},
		{
			name: "long_arrowhead_label",
			script: `
a -> b: {
	target-arrowhead: "a to b with unexpectedly long target arrowhead label"
}
`,
		},
		{
			name: "arrowhead_sizes_with_labels",
			script: `
triangle: {
	a <-> b: {
		source-arrowhead: 1
		target-arrowhead: 1
	}
	c <-> d: {
		source-arrowhead: 1
		target-arrowhead: 1
		style.stroke-width: 8
	}
}
none: {
	a -- b: {
		source-arrowhead: 1
		target-arrowhead: 1
	}
	c -- d: {
		source-arrowhead: 1
		target-arrowhead: 1
		style.stroke-width: 8
	}
}
arrow: {
	a <-> b: {
		source-arrowhead: 1 {
			shape: arrow
		}
		target-arrowhead: 1 {
			shape: arrow
		}
	}
	c <-> d: {
		source-arrowhead: 1 {
			shape: arrow
		}
		target-arrowhead: 1 {
			shape: arrow
		}
		style.stroke-width: 8
	}
}
diamond: {
	a <-> b: {
		source-arrowhead: 1 {
			shape: diamond
		}
		target-arrowhead: 1 {
			shape: diamond
		}
	}
	c <-> d: {
		source-arrowhead: 1 {
			shape: diamond
		}
		target-arrowhead: 1 {
			shape: diamond
		}
		style.stroke-width: 8
	}
}
filled diamond: {
	a <-> b: {
		source-arrowhead: 1 {
			shape: diamond
			style.filled: true
		}
		target-arrowhead: 1 {
			shape: diamond
			style.filled: true
		}
	}
	c <-> d: {
		source-arrowhead: 1 {
			shape: diamond
			style.filled: true
		}
		target-arrowhead: 1 {
			shape: diamond
			style.filled: true
		}
		style.stroke-width: 8
	}
}
circle: {
	a <-> b: {
		source-arrowhead: 1 {
			shape: circle
		}
		target-arrowhead: 1 {
			shape: circle
		}
	}
	c <-> d: {
		source-arrowhead: 1 {
			shape: circle
		}
		target-arrowhead: 1 {
			shape: circle
		}
		style.stroke-width: 8
	}
}
filled circle: {
	a <-> b: {
		source-arrowhead: 1 {
			shape: circle
			style.filled: true
		}
		target-arrowhead: 1 {
			shape: circle
			style.filled: true
		}
	}
	c <-> d: {
		source-arrowhead: 1 {
			shape: circle
			style.filled: true
		}
		target-arrowhead: 1 {
			shape: circle
			style.filled: true
		}
		style.stroke-width: 8
	}
}
cf one: {
	a <-> b: {
		source-arrowhead: 1 {
			shape: cf-one
		}
		target-arrowhead: 1 {
			shape: cf-one
		}
	}
	c <-> d: {
		source-arrowhead: 1 {
			shape: cf-one
		}
		target-arrowhead: 1 {
			shape: cf-one
		}
		style.stroke-width: 8
	}
}
cf one required: {
	a <-> b: {
		source-arrowhead: 1 {
			shape: cf-one-required
		}
		target-arrowhead: 1 {
			shape: cf-one-required
		}
	}
	c <-> d: {
		source-arrowhead: 1 {
			shape: cf-one-required
		}
		target-arrowhead: 1 {
			shape: cf-one-required
		}
		style.stroke-width: 8
	}
}
cf many: {
	a <-> b: {
		source-arrowhead: 1 {
			shape: cf-many
		}
		target-arrowhead: 1 {
			shape: cf-many
		}
	}
	c <-> d: {
		source-arrowhead: 1 {
			shape: cf-many
		}
		target-arrowhead: 1 {
			shape: cf-many
		}
		style.stroke-width: 8
	}
}
cf many required: {
	a <-> b: {
		source-arrowhead: 1 {
			shape: cf-many-required
		}
		target-arrowhead: 1 {
			shape: cf-many-required
		}
	}
	c <-> d: {
		source-arrowhead: 1 {
			shape: cf-many-required
		}
		target-arrowhead: 1 {
			shape: cf-many-required
		}
		style.stroke-width: 8
	}
}
`,
		},
		{
			name:   "dagre_child_id_id",
			script: `direction:right; id -> x.id -> y.z.id`,
		},
		loadFromFile(t, "slow_grid"),
		loadFromFile(t, "grid_oom"),
		loadFromFile(t, "cylinder_grid_label"),
		loadFromFile(t, "grid_with_latex"),
		loadFromFile(t, "icons_on_top"),
		loadFromFile(t, "dagre_disconnected_edge"),
		loadFromFile(t, "outside_grid_label_position"),
		loadFromFile(t, "arrowhead_font_color"),
		loadFromFile(t, "multiple_constant_nears"),
		loadFromFile(t, "empty_nested_grid"),
		loadFromFile(t, "code_font_size"),
		loadFromFile(t, "disclaimer"),
		loadFromFile(t, "grid_rows_gap_bug"),
		loadFromFile(t, "grid_image_label_position"),
		loadFromFile(t, "glob_dimensions"),
		loadFromFile(t, "shaped_grid_positioning"),
		loadFromFile(t, "cloud_shaped_grid"),
		loadFromFileWithOptions(t, "nested_layout_bug", testCase{testSerialization: true}),
		loadFromFile(t, "disconnect_direction_right"),
	}

	runa(t, tcs)
}
