package composer

import (
	"os"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
)

var (
	// docker run --name composer --rm -it composer@sha256:082ed124b68e7e880721772a6bf22ad809e3bc87db8bbee9f0ec7127bb21ccad bash
	// apk add jq
	// composer require guzzlehttp/guzzle:6.5.8
	// composer require pear/log:1.13.3 --dev
	// composer show -i --no-dev -f json | jq --sort-keys -rc '.installed[] | "{ID: \"\(.name)@\(.version)\", Name: \"\(.name)\", Version: \"\(.version)\", License: \"MIT\", Locations: []ftypes.Location{{StartLine: , EndLine: }}},"'
	// locations are filled manually
	composerPkgs = []ftypes.Package{
		{
			ID:       "guzzlehttp/guzzle@6.5.8",
			Name:     "guzzlehttp/guzzle",
			Version:  "6.5.8",
			Licenses: []string{"MIT"},
			Locations: []ftypes.Location{
				{
					StartLine: 9,
					EndLine:   123,
				},
			},
		},
		{
			ID:       "guzzlehttp/promises@1.5.2",
			Name:     "guzzlehttp/promises",
			Version:  "1.5.2",
			Licenses: []string{"MIT"},
			Locations: []ftypes.Location{
				{
					StartLine: 124,
					EndLine:   207,
				},
			},
		},
		{
			ID:       "guzzlehttp/psr7@1.9.0",
			Name:     "guzzlehttp/psr7",
			Version:  "1.9.0",
			Licenses: []string{"MIT"},
			Locations: []ftypes.Location{
				{
					StartLine: 208,
					EndLine:   317,
				},
			},
		},
		{
			ID:       "pear/log@1.13.3",
			Name:     "pear/log",
			Version:  "1.13.3",
			Dev:      true,
			Licenses: []string{"MIT"},
			Locations: []ftypes.Location{
				{
					StartLine: 660,
					EndLine:   719,
				},
			},
		},
		{
			ID:       "pear/pear_exception@v1.0.2",
			Name:     "pear/pear_exception",
			Version:  "v1.0.2",
			Dev:      true,
			Licenses: []string{"BSD-2-Clause"},
			Locations: []ftypes.Location{
				{
					StartLine: 720,
					EndLine:   778,
				},
			},
		},
		{
			ID:       "psr/http-message@1.0.1",
			Name:     "psr/http-message",
			Version:  "1.0.1",
			Licenses: []string{"MIT"},
			Locations: []ftypes.Location{
				{
					StartLine: 318,
					EndLine:   370,
				},
			},
		},
		{
			ID:       "ralouphie/getallheaders@3.0.3",
			Name:     "ralouphie/getallheaders",
			Version:  "3.0.3",
			Licenses: []string{"MIT"},
			Locations: []ftypes.Location{
				{
					StartLine: 371,
					EndLine:   414,
				},
			},
		},
		{
			ID:       "symfony/polyfill-intl-idn@v1.27.0",
			Name:     "symfony/polyfill-intl-idn",
			Version:  "v1.27.0",
			Licenses: []string{"MIT"},
			Locations: []ftypes.Location{
				{
					StartLine: 415,
					EndLine:   501,
				},
			},
		},
		{
			ID:       "symfony/polyfill-intl-normalizer@v1.27.0",
			Name:     "symfony/polyfill-intl-normalizer",
			Version:  "v1.27.0",
			Licenses: []string{"MIT"},
			Locations: []ftypes.Location{
				{
					StartLine: 502,
					EndLine:   583,
				},
			},
		},
		{
			ID:       "symfony/polyfill-php72@v1.27.0",
			Name:     "symfony/polyfill-php72",
			Version:  "v1.27.0",
			Licenses: []string{"MIT", "BSD-2-Clause"},
			Locations: []ftypes.Location{
				{
					StartLine: 584,
					EndLine:   657,
				},
			},
		},
	}
	// dependencies are filled manually
	composerDeps = []ftypes.Dependency{
		{
			ID: "guzzlehttp/guzzle@6.5.8",
			DependsOn: []string{
				"guzzlehttp/promises@1.5.2",
				"guzzlehttp/psr7@1.9.0",
				"symfony/polyfill-intl-idn@v1.27.0",
			},
		},
		{
			ID: "guzzlehttp/psr7@1.9.0",
			DependsOn: []string{
				"psr/http-message@1.0.1",
				"ralouphie/getallheaders@3.0.3",
			},
		},
		{
			ID: "pear/log@1.13.3",
			DependsOn: []string{
				"pear/pear_exception@v1.0.2",
			},
		},
		{
			ID: "symfony/polyfill-intl-idn@v1.27.0",
			DependsOn: []string{
				"symfony/polyfill-intl-normalizer@v1.27.0",
				"symfony/polyfill-php72@v1.27.0",
			},
		},
	}
)

func TestParse(t *testing.T) {
	tests := []struct {
		name     string
		file     string
		wantPkgs []ftypes.Package
		wantDeps []ftypes.Dependency
	}{
		{
			name:     "happy path",
			file:     "testdata/composer_happy.lock",
			wantPkgs: composerPkgs,
			wantDeps: composerDeps,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			f, err := os.Open(tt.file)
			require.NoError(t, err)
			defer f.Close()

			gotPkgs, gotDeps, err := NewParser().Parse(t.Context(), f)
			require.NoError(t, err)

			assert.Equal(t, tt.wantPkgs, gotPkgs)
			assert.Equal(t, tt.wantDeps, gotDeps)
		})
	}
}
