package changelog_test

import (
	"testing"

	"github.com/Nightapes/go-semantic-release/internal/analyzer"
	"github.com/Nightapes/go-semantic-release/internal/changelog"
	"github.com/Nightapes/go-semantic-release/internal/shared"
	"github.com/Nightapes/go-semantic-release/pkg/config"
	"github.com/stretchr/testify/assert"
	"time"
)

func TestChangelog(t *testing.T) {

	templateConfig := shared.ChangelogTemplateConfig{
		CommitURL:  "https://commit.url",
		CompareURL: "https://compare.url",
		Hash:       "hash",
		Version:    "1.0.0",
	}

	testConfigs := []struct {
		testCase        string
		analyzedCommits map[shared.Release][]shared.AnalyzedCommit
		result          *shared.GeneratedChangelog
		hasError        bool
		showAuthors     bool
	}{
		{
			testCase: "feat",
			analyzedCommits: map[shared.Release][]shared.AnalyzedCommit{
				"minor": {
					{
						Commit: shared.Commit{
							Message: "feat(internal/changelog): my first commit",
							Author:  "me",
							Hash:    "12345667",
						},
						Scope:         "internal/changelog",
						ParsedMessage: "my first commit",
						Tag:           "feat",
						TagString:     "Features",
						Print:         true,
						Subject:       "my first commit",
						MessageBlocks: map[string][]shared.MessageBlock{},
					},
				},
			},
			result: &shared.GeneratedChangelog{
				Title:   "v1.0.0 (2019-07-19)",
				Content: "# v1.0.0 (2019-07-19)\n### Features\n* **`internal/changelog`** my first commit ([1234566](https://commit.url))\n",
			},
			hasError: false,
		},
		{
			testCase:    "feat with authors",
			showAuthors: true,
			analyzedCommits: map[shared.Release][]shared.AnalyzedCommit{
				"minor": {
					{
						Commit: shared.Commit{
							Message: "feat(internal/changelog): my first commit",
							Author:  "me",
							Hash:    "12345667",
						},
						Scope:         "internal/changelog",
						ParsedMessage: "my first commit",
						Tag:           "feat",
						TagString:     "Features",
						Print:         true,
						Subject:       "my first commit",
						MessageBlocks: map[string][]shared.MessageBlock{},
					},
					{
						Commit: shared.Commit{
							Message: "feat(internal/changelog): my second commit",
							Author:  "secondAuthor",
							Hash:    "12345667",
						},
						Scope:         "internal/changelog",
						ParsedMessage: "my second commit",
						Tag:           "feat",
						TagString:     "Features",
						Print:         true,
						Subject:       "my second commit",
						MessageBlocks: map[string][]shared.MessageBlock{},
					},
				},
			},
			result: &shared.GeneratedChangelog{
				Title:   "v1.0.0 (2019-07-19)",
				Content: "# v1.0.0 (2019-07-19)\n### Features\n* **`internal/changelog`** my first commit ([1234566](https://commit.url))\n* **`internal/changelog`** my second commit ([1234566](https://commit.url))\n# Special Thanks\n\nme, secondAuthor\n"},
			hasError: false,
		},
		{
			testCase: "feat no scope",
			analyzedCommits: map[shared.Release][]shared.AnalyzedCommit{
				"minor": {
					{
						Commit: shared.Commit{
							Message: "feat: my first commit",
							Author:  "me",
							Hash:    "12345667",
						},
						ParsedMessage: "my first commit",
						Tag:           "feat",
						TagString:     "Features",
						Print:         true,
						Subject:       "my first commit",
						MessageBlocks: map[string][]shared.MessageBlock{},
					},
				},
			},
			result: &shared.GeneratedChangelog{
				Title:   "v1.0.0 (2019-07-19)",
				Content: "# v1.0.0 (2019-07-19)\n### Features\n* my first commit ([1234566](https://commit.url))\n",
			},
			hasError: false,
		},
		{
			testCase: "feat breaking change",
			analyzedCommits: map[shared.Release][]shared.AnalyzedCommit{
				"minor": {
					{
						Commit: shared.Commit{
							Message: "feat(internal/changelog): my first commit",
							Author:  "me",
							Hash:    "12345667",
						},
						Scope:         "internal/changelog",
						ParsedMessage: "my first commit",
						Tag:           "feat",
						TagString:     "Features",
						Print:         true,
						Subject:       "my first commit",
						MessageBlocks: map[string][]shared.MessageBlock{},
					},
					{
						Commit: shared.Commit{
							Message: "feat(internal/changelog): my first break: BREAKING CHANGE: change api to v2",
							Author:  "me",
							Hash:    "12345668",
						},
						Scope:                       "internal/changelog",
						ParsedMessage:               "my first break",
						Tag:                         "feat",
						TagString:                   "Features",
						Print:                       true,
						ParsedBreakingChangeMessage: "change api to v2",
						IsBreaking:                  true,
						Subject:                     "my first break",
						MessageBlocks: map[string][]shared.MessageBlock{
							"body": {shared.MessageBlock{
								Label:   "BREAKING CHANGE",
								Content: "change api to v2",
							},
							},
						},
					},
				},
			},
			result: &shared.GeneratedChangelog{
				Title:   "v1.0.0 (2019-07-19)",
				Content: "# v1.0.0 (2019-07-19)\n## BREAKING CHANGES\n* **`internal/changelog`** change api to v2  \nintroduced by commit: \nmy first break  ([1234566](https://commit.url))\n### Features\n* **`internal/changelog`** my first commit ([1234566](https://commit.url))\n",
			},
			hasError: false,
		},
		{
			testCase: "conventional commits",
			analyzedCommits: map[shared.Release][]shared.AnalyzedCommit{
				"minor": {
					{
						Commit: shared.Commit{
							Message: "feat!: my first break \n\nBREAKING CHANGE: hey from the change",
							Author:  "me",
							Hash:    "12345669",
						},
						Scope:                       "",
						ParsedMessage:               "my first break",
						Tag:                         "feat",
						TagString:                   "Features",
						Print:                       true,
						ParsedBreakingChangeMessage: "hey from the change",
						IsBreaking:                  true,
						Subject:                     "my first break",
						MessageBlocks: map[string][]shared.MessageBlock{
							"body": {shared.MessageBlock{
								Label:   "BREAKING CHANGE",
								Content: "hey from the change",
							},
							},
						},
					},
					{
						Commit: shared.Commit{
							Message: "feat(internal/changelog): my first commit",
							Author:  "me",
							Hash:    "12345667",
						},
						Scope:         "internal/changelog",
						ParsedMessage: "my first commit",
						Tag:           "feat",
						TagString:     "Features",
						Print:         true,
						Subject:       "my first commit",
						MessageBlocks: map[string][]shared.MessageBlock{},
					},
					{
						Commit: shared.Commit{
							Message: "feat: my second commit",
							Author:  "me",
							Hash:    "12345667",
						},
						Scope:         "",
						ParsedMessage: "my first commit",
						Tag:           "feat",
						TagString:     "Features",
						Print:         true,
						Subject:       "my second commit",
						MessageBlocks: map[string][]shared.MessageBlock{},
					},
					{
						Commit: shared.Commit{
							Message: "feat: my first break \n\nBREAKING CHANGE: change api to v2",
							Author:  "me",
							Hash:    "12345668",
						},
						Scope:                       "",
						ParsedMessage:               "my first break",
						Tag:                         "feat",
						TagString:                   "Features",
						Print:                       true,
						ParsedBreakingChangeMessage: "change api to v2",
						IsBreaking:                  true,
						Subject:                     "my first break",
						MessageBlocks: map[string][]shared.MessageBlock{
							"body": {shared.MessageBlock{
								Label:   "BREAKING CHANGE",
								Content: "change api to v2",
							}},
						},
					},
					{
						Commit: shared.Commit{
							Message: "feat: my awesome features \n\n * Feature1: Lists in changelog \n* Feature2: Lists in changelog2",
							Author:  "me",
							Hash:    "12345668",
						},
						Scope:                       "",
						ParsedMessage:               "my awesome features",
						Tag:                         "feat",
						TagString:                   "Features",
						Print:                       true,
						ParsedBreakingChangeMessage: "",
						IsBreaking:                  false,
						Subject:                     "my awesome features",
						MessageBlocks: map[string][]shared.MessageBlock{
							"body": {shared.MessageBlock{
								Label:   "",
								Content: "* Feature1: Lists in changelog \n* Feature2: Lists in changelog2",
							}},
						},
					},
					{
						Commit: shared.Commit{
							Message: "feat!: my next commit",
							Author:  "me",
							Hash:    "12345668",
						},
						Scope:                       "",
						ParsedMessage:               "",
						Tag:                         "feat",
						TagString:                   "Features",
						Print:                       true,
						ParsedBreakingChangeMessage: "my next commit",
						IsBreaking:                  true,
						Subject:                     "my next commit",
						MessageBlocks:               map[string][]shared.MessageBlock{},
					},
				},
			},
			result: &shared.GeneratedChangelog{
				Title:   "v1.0.0 (2019-07-19)",
				Content: "# v1.0.0 (2019-07-19)\n## BREAKING CHANGES\n*  hey from the change  \nintroduced by commit: \nmy first break  ([1234566](https://commit.url))\n*  change api to v2  \nintroduced by commit: \nmy first break  ([1234566](https://commit.url))\n*  my next commit  \nintroduced by commit: \nmy next commit  ([1234566](https://commit.url))\n### Features\n* **`internal/changelog`** my first commit ([1234566](https://commit.url))\n* my second commit ([1234566](https://commit.url))\n* my awesome features ([1234566](https://commit.url))\n  > * Feature1: Lists in changelog \n  > * Feature2: Lists in changelog2\n"},
			hasError: false,
		},
	}

	for _, testConfig := range testConfigs {
		t.Run(testConfig.testCase, func(t *testing.T) {
			cl := changelog.New(&config.ReleaseConfig{
				Changelog: config.ChangelogConfig{
					ShowBodyAsHeader: false,
					ShowAuthors:      testConfig.showAuthors,
				},
			}, []analyzer.Rule{
				{
					Tag:       "feat",
					TagString: "Features",
					Release:   "minor",
					Changelog: true,
				},
				{
					Tag:       "fix",
					TagString: "Bug fixes",
					Release:   "patch",
					Changelog: true,
				},
				{
					Tag:       "build",
					TagString: "Build",
					Release:   "none",
					Changelog: false,
				},
			}, time.Date(2019, 7, 19, 0, 0, 0, 0, time.UTC))

			generatedChangelog, err := cl.GenerateChangelog(templateConfig, testConfig.analyzedCommits)
			assert.Equalf(t, testConfig.hasError, err != nil, "Testcase %s should have error: %t -> %s", testConfig.testCase, testConfig.hasError, err)
			assert.Equalf(t, testConfig.result, generatedChangelog, "Testcase %s should have generated changelog", testConfig.testCase)
		})
	}

}

func TestChangelogExtensions(t *testing.T) {

	testConfigs := []struct {
		testCase      string
		result        *shared.GeneratedChangelog
		releaseConfig *config.ReleaseConfig
	}{
		{
			testCase: "docker",
			releaseConfig: &config.ReleaseConfig{
				Changelog: config.ChangelogConfig{
					Docker: config.ChangelogDocker{
						Latest:     true,
						Repository: "mydocker.de",
					},
					NPM: config.ChangelogNPM{},
				},
			},
			result: &shared.GeneratedChangelog{Title: "v1.0.0 (2019-07-19)", Content: "# v1.0.0 (2019-07-19)\n### Features\n* **`internal/changelog`** my first commit ([1234566](https://commit.url))\n\n## Docker image\n\nNew docker image is released under `mydocker.de:1.0.0`\n\n### Usage\n\n`docker run mydocker.de:1.0.0`\n\nor\n\n`docker run mydocker.de:latest`\n"},
		},
		{
			testCase: "npm",
			releaseConfig: &config.ReleaseConfig{
				Changelog: config.ChangelogConfig{
					Docker: config.ChangelogDocker{},
					NPM: config.ChangelogNPM{
						Repository:  "https://github.com/Nightapes/ngx-validators/packages/102720",
						PackageName: "ngx-validators",
					},
				},
			},
			result: &shared.GeneratedChangelog{Title: "v1.0.0 (2019-07-19)", Content: "# v1.0.0 (2019-07-19)\n### Features\n* **`internal/changelog`** my first commit ([1234566](https://commit.url))\n\n## NodeJS Package\n\nNew NodeJS package is released under [ngx-validators](https://github.com/Nightapes/ngx-validators/packages/102720)\n\n### Usage\n\n`yarn add ngx-validators@1.0.0`\n\nor\n\n`npm install -save ngx-validators@1.0.0`\n\n"},
		},
	}

	analyzedCommits := map[shared.Release][]shared.AnalyzedCommit{
		"minor": {
			{
				Commit: shared.Commit{
					Message: "feat(internal/changelog): my first commit",
					Author:  "me",
					Hash:    "12345667",
				},
				Scope:         "internal/changelog",
				ParsedMessage: "my first commit",
				Tag:           "feat",
				TagString:     "Features",
				Print:         true,
				Subject:       "my first commit",
				MessageBlocks: map[string][]shared.MessageBlock{},
			},
		},
	}

	for _, config := range testConfigs {
		t.Run(config.testCase, func(t *testing.T) {
			templateConfig := shared.ChangelogTemplateConfig{
				CommitURL:  "https://commit.url",
				CompareURL: "https://compare.url",
				Hash:       "hash",
				Version:    "1.0.0",
			}
			cl := changelog.New(config.releaseConfig, []analyzer.Rule{
				{
					Tag:       "feat",
					TagString: "Features",
					Release:   "minor",
					Changelog: true,
				},
				{
					Tag:       "fix",
					TagString: "Bug fixes",
					Release:   "patch",
					Changelog: true,
				},
				{
					Tag:       "build",
					TagString: "Build",
					Release:   "none",
					Changelog: false,
				},
			}, time.Date(2019, 7, 19, 0, 0, 0, 0, time.UTC))
			generatedChangelog, err := cl.GenerateChangelog(templateConfig, analyzedCommits)
			assert.NoError(t, err)
			assert.Equalf(t, config.result, generatedChangelog, "Testcase %s should have generated changelog", config.testCase)
		})
	}

}
