package sequencediagram

import (
	"fmt"
	"testing"

	"github.com/anz-bank/sysl/pkg/cmdutils"
	"github.com/anz-bank/sysl/pkg/parse"
	"github.com/anz-bank/sysl/pkg/syslutil"
	"github.com/sirupsen/logrus/hooks/test"
	"github.com/spf13/afero"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

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

	_, err := parse.NewParser().ParseFromFs("doesn't-exist.sysl", syslutil.NewChrootFs(afero.NewOsFs(), ""))
	require.Error(t, err)
}

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

	logger, _ := test.NewNullLogger()
	m, err := parse.NewParser().ParseFromFs("tests/ditto2.sysl",
		syslutil.NewChrootFs(afero.NewOsFs(), projDir))
	require.NoError(t, err)
	l := &cmdutils.Labeler{}
	p := &SequenceDiagParam{Title: "Profile"}
	p.Endpoints = []string{"foo <- fooEndpoint"}
	p.AppLabeler = l
	p.EndpointLabeler = l
	r, err := GenerateSequenceDiag(m, p, logger)
	assert.NotNil(t, m)
	assert.NotNil(t, r)
	assert.NoError(t, err)
	expected := `''''''''''''''''''''''''''''''''''''''''''
''                                      ''
''  AUTOGENERATED CODE -- DO NOT EDIT!  ''
''                                      ''
''''''''''''''''''''''''''''''''''''''''''

@startuml
control "foo" as _0
control "bar" as _1
skinparam maxMessageSize 250
title Profile
== foo <- fooEndpoint ==
[->_0 : fooEndpoint
activate _0
 _0->_1 : barEndpoint
 activate _1
 _0<--_1 : barType
 deactivate _1
[<--_0 : fooType
deactivate _0
@enduml
`
	assert.Equal(t, expected, r)
}

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

	logger, _ := test.NewNullLogger()
	m, err := parse.NewParser().ParseFromFs("tests/hidden.sysl",
		syslutil.NewChrootFs(afero.NewOsFs(), projDir))
	require.NoError(t, err)
	l := &cmdutils.Labeler{}
	p := &SequenceDiagParam{Title: "Profile"}
	p.Endpoints = []string{"App <- Endpoint1"}
	p.AppLabeler = l
	p.EndpointLabeler = l
	r, err := GenerateSequenceDiag(m, p, logger)
	assert.NotNil(t, m)
	assert.NotNil(t, r)
	assert.NoError(t, err)
	expected := `''''''''''''''''''''''''''''''''''''''''''
''                                      ''
''  AUTOGENERATED CODE -- DO NOT EDIT!  ''
''                                      ''
''''''''''''''''''''''''''''''''''''''''''

@startuml
control "App" as _0
control "SecondApp" as _1
skinparam maxMessageSize 250
title Profile
== App <- Endpoint1 ==
[->_0 : Endpoint1
activate _0
 activate _1
 _0<--_1 : whatever
 deactivate _1
deactivate _0
@enduml
`
	assert.Equal(t, expected, r)
}

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

	logger, _ := test.NewNullLogger()
	m, err := parse.NewParser().ParseFromFs("demo/simple/sysl-endpoint-hyperlink.sysl",
		syslutil.NewChrootFs(afero.NewOsFs(), projDir))
	require.NoError(t, err)
	l := &cmdutils.Labeler{}
	p := &SequenceDiagParam{Title: "Profile"}
	p.Endpoints = []string{"MobileApp <- Login"}
	p.AppLabeler = l
	p.EndpointLabeler = l
	r, err := GenerateSequenceDiag(m, p, logger)

	expected := `''''''''''''''''''''''''''''''''''''''''''
''                                      ''
''  AUTOGENERATED CODE -- DO NOT EDIT!  ''
''                                      ''
''''''''''''''''''''''''''''''''''''''''''

@startuml
control "MobileApp" as _0
control "Server" as _1
skinparam maxMessageSize 250
title Profile
== MobileApp <- [[https://www.google.com Login]] ==
[->_0 : Login
activate _0
 _0->_1 : LoginRequest
 activate _1
 _0<--_1 : <color blue>MobileApp.LoginResponse</color> <<color green>?, ?</color>>
 deactivate _1
deactivate _0
@enduml
`
	assert.NotNil(t, m)
	assert.NotNil(t, r)
	assert.NoError(t, err)
	assert.Equal(t, expected, r)
}

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

	logger, _ := test.NewNullLogger()
	m, err := parse.NewParser().ParseFromFs("demo/simple/sysl-app-hyperlink.sysl",
		syslutil.NewChrootFs(afero.NewOsFs(), projDir))
	require.NoError(t, err)
	l := &cmdutils.Labeler{}
	p := &SequenceDiagParam{Title: "Profile"}
	p.Endpoints = []string{"MobileApp <- Login"}
	p.AppLabeler = l
	p.EndpointLabeler = l
	r, err := GenerateSequenceDiag(m, p, logger)

	expected := `''''''''''''''''''''''''''''''''''''''''''
''                                      ''
''  AUTOGENERATED CODE -- DO NOT EDIT!  ''
''                                      ''
''''''''''''''''''''''''''''''''''''''''''

@startuml
control "MobileApp" as _0
control "Server" as _1
skinparam maxMessageSize 250
title Profile
== [[https://www.foofle.com MobileApp]] <- Login ==
[->_0 : Login
activate _0
 _0->_1 : LoginRequest
 activate _1
 _0<--_1 : <color blue>MobileApp.LoginResponse</color> <<color green>?, ?</color>>
 deactivate _1
deactivate _0
@enduml
`
	assert.NotNil(t, m)
	assert.NotNil(t, r)
	assert.NoError(t, err)
	assert.Equal(t, expected, r)
}

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

	logger, _ := test.NewNullLogger()
	m, err := parse.NewParser().ParseFromFs("demo/simple/sysl-endpoint-app-hyperlink.sysl",
		syslutil.NewChrootFs(afero.NewOsFs(), projDir))
	require.NoError(t, err)
	l := &cmdutils.Labeler{}
	p := &SequenceDiagParam{Title: "Profile"}
	p.Endpoints = []string{"MobileApp <- Login"}
	p.AppLabeler = l
	p.EndpointLabeler = l
	r, err := GenerateSequenceDiag(m, p, logger)

	expected := `''''''''''''''''''''''''''''''''''''''''''
''                                      ''
''  AUTOGENERATED CODE -- DO NOT EDIT!  ''
''                                      ''
''''''''''''''''''''''''''''''''''''''''''

@startuml
control "MobileApp" as _0
control "Server" as _1
skinparam maxMessageSize 250
title Profile
== [[https://www.foofle.com MobileApp]] <- [[https://www.google.com Login]] ==
[->_0 : Login
activate _0
 _0->_1 : LoginRequest
 activate _1
 _0<--_1 : <color blue>MobileApp.LoginResponse</color> <<color green>?, ?</color>>
 deactivate _1
deactivate _0
@enduml
`
	fmt.Print(r)
	assert.NotNil(t, m)
	assert.NotNil(t, r)
	assert.NoError(t, err)
	assert.Equal(t, expected, r)
}

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

	logger, _ := test.NewNullLogger()
	m, err := parse.NewParser().ParseFromFs("demo/simple/sysl-sd.sysl",
		syslutil.NewChrootFs(afero.NewOsFs(), projDir))
	require.NoError(t, err)
	l := &cmdutils.Labeler{}
	p := &SequenceDiagParam{Title: "Profile"}
	p.Endpoints = []string{"WebFrontend <- RequestProfile"}
	p.AppLabeler = l
	p.EndpointLabeler = l
	r, err := GenerateSequenceDiag(m, p, logger)

	expected := `''''''''''''''''''''''''''''''''''''''''''
''                                      ''
''  AUTOGENERATED CODE -- DO NOT EDIT!  ''
''                                      ''
''''''''''''''''''''''''''''''''''''''''''

@startuml
control "WebFrontend" as _0
control "Api" as _1
database "Database" as _2
skinparam maxMessageSize 250
title Profile
== WebFrontend <- RequestProfile ==
[->_0 : RequestProfile
activate _0
 _0->_1 : GET /users/{user_id}/profile
 activate _1
  _1->_2 : QueryUser
  activate _2
  _1<--_2 : User
  deactivate _2
 _0<--_1 : UserProfile
 deactivate _1
[<--_0 : Profile Page
deactivate _0
@enduml
`

	assert.NotNil(t, m)
	assert.NotNil(t, r)
	assert.NoError(t, err)
	assert.Equal(t, expected, r)
}

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

	logger, _ := test.NewNullLogger()
	m, err := parse.NewParser().ParseFromFs("demo/simple/sysl-sd2.sysl",
		syslutil.NewChrootFs(afero.NewOsFs(), projDir))
	require.NoError(t, err)
	l := &cmdutils.Labeler{}
	p := &SequenceDiagParam{Title: "Profile"}
	p.Endpoints = []string{"WebFrontend <- RequestProfile"}
	p.AppLabeler = l
	p.EndpointLabeler = l
	r, err := GenerateSequenceDiag(m, p, logger)

	expected := `''''''''''''''''''''''''''''''''''''''''''
''                                      ''
''  AUTOGENERATED CODE -- DO NOT EDIT!  ''
''                                      ''
''''''''''''''''''''''''''''''''''''''''''

@startuml
control "WebFrontend" as _0
control "Api" as _1
database "Database" as _2
skinparam maxMessageSize 250
title Profile
== WebFrontend <- RequestProfile ==
[->_0 : RequestProfile
activate _0
 _0->_1 : GET /users/{user_id}/profile
 activate _1
  _1->_2 : QueryUser
  activate _2
  _1<--_2 : User [~y |  x="1"]
  deactivate _2
 _0<--_1 : UserProfile
 deactivate _1
 _0->_0 : FooBar
[<--_0 : Profile Page
deactivate _0
@enduml
`

	assert.NotNil(t, m)
	assert.NotNil(t, r)
	assert.NoError(t, err)
	assert.Equal(t, expected, r)
}

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

	logger, _ := test.NewNullLogger()
	memFs, fs := syslutil.WriteToMemOverlayFs(testDir)
	m, err := parse.NewParser().ParseFromFs("sequence_diagram_name_format.sysl", fs)
	require.NoError(t, err)
	syslutil.AssertFsHasExactly(t, memFs)
	al := cmdutils.MakeFormatParser(`%(@status?<color red>%(appname)</color>|%(appname))`)
	el := cmdutils.MakeFormatParser(`%(@status? <color green>%(epname)</color>|%(epname))`)
	p := &SequenceDiagParam{}
	p.Endpoints = []string{"User <- Check Balance"}
	p.AppLabeler = al
	p.EndpointLabeler = el
	r, err := GenerateSequenceDiag(m, p, logger)
	p.Title = "Diagram"
	expected := `''''''''''''''''''''''''''''''''''''''''''
''                                      ''
''  AUTOGENERATED CODE -- DO NOT EDIT!  ''
''                                      ''
''''''''''''''''''''''''''''''''''''''''''

@startuml
actor "User" as _0
boundary "MobileApp" as _1
control "<color red>Server</color>" as _2
database "DB" as _3
collections "Statement" as _5
queue "AuditTopic" as _4
skinparam maxMessageSize 250
== User <- Check Balance ==
 _0->_1 : Login
 activate _1
  _1->_2 : Login
  activate _2
  _2 -> _2 : do input validation
   _2->_3 :  <color green>Save</color>
   _2->_4 : Publish
  _1<--_2 : ok
  _1<--_2 : error
  deactivate _2
 deactivate _1
 _0->_1 : Check Balance
 activate _1
  _1->_2 : Read User Balance
  activate _2
   _2->_3 :  <color green>Load</color>
   _2->_5 : Download
   activate _5
   _2<--_5 : pdf
   deactivate _5
  _1<--_2 : balance
  deactivate _2
 deactivate _1
@enduml
`

	assert.NotNil(t, m)
	assert.NotNil(t, r)
	assert.NoError(t, err)
	assert.Equal(t, expected, r)
}

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

	logger, _ := test.NewNullLogger()
	memFs, fs := syslutil.WriteToMemOverlayFs(testDir)
	m, err := parse.NewParser().ParseFromFs("sequence_diagram_name_format.sysl", fs)
	require.NoError(t, err)
	syslutil.AssertFsHasExactly(t, memFs)
	al := cmdutils.MakeFormatParser(`%(@status?<color red>%(appname)</color>|%(appname))`)
	el := cmdutils.MakeFormatParser(`%(@status? <color green>%(epname)</color>|%(epname))`)
	p := &SequenceDiagParam{}
	p.Endpoints = []string{"User <- Check Balance"}
	p.AppLabeler = al
	p.EndpointLabeler = el
	r, err := GenerateSequenceDiag(m, p, logger)
	p.Title = "Diagram"
	expected := `''''''''''''''''''''''''''''''''''''''''''
''                                      ''
''  AUTOGENERATED CODE -- DO NOT EDIT!  ''
''                                      ''
''''''''''''''''''''''''''''''''''''''''''

@startuml
actor "User" as _0
boundary "MobileApp" as _1
control "<color red>Server</color>" as _2
database "DB" as _3
collections "Statement" as _5
queue "AuditTopic" as _4
skinparam maxMessageSize 250
== User <- Check Balance ==
 _0->_1 : Login
 activate _1
  _1->_2 : Login
  activate _2
  _2 -> _2 : do input validation
   _2->_3 :  <color green>Save</color>
   _2->_4 : Publish
  _1<--_2 : ok
  _1<--_2 : error
  deactivate _2
 deactivate _1
 _0->_1 : Check Balance
 activate _1
  _1->_2 : Read User Balance
  activate _2
   _2->_3 :  <color green>Load</color>
   _2->_5 : Download
   activate _5
   _2<--_5 : pdf
   deactivate _5
  _1<--_2 : balance
  deactivate _2
 deactivate _1
@enduml
`

	assert.NotNil(t, m)
	assert.NotNil(t, r)
	assert.NoError(t, err)
	assert.Equal(t, expected, r)
}
