package plugin

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"github.com/terraform-linters/tflint/tflint"
)

func Test_GetSigningKey(t *testing.T) {
	cases := []struct {
		Name     string
		Config   *InstallConfig
		Expected string
	}{
		{
			Name:     "no signing key",
			Config:   NewInstallConfig(tflint.EmptyConfig(), &tflint.PluginConfig{SigningKey: ""}),
			Expected: "",
		},
		{
			Name:     "configured singing key",
			Config:   NewInstallConfig(tflint.EmptyConfig(), &tflint.PluginConfig{SigningKey: testSigningKey}),
			Expected: testSigningKey,
		},
		{
			Name:     "bulit-in signing key",
			Config:   NewInstallConfig(tflint.EmptyConfig(), &tflint.PluginConfig{SigningKey: "", SourceOwner: "terraform-linters"}),
			Expected: builtinSigningKey,
		},
		{
			Name:     "bulit-in signing key and configured signing key",
			Config:   NewInstallConfig(tflint.EmptyConfig(), &tflint.PluginConfig{SigningKey: testSigningKey, SourceOwner: "terraform-linters"}),
			Expected: testSigningKey,
		},
	}

	for _, tc := range cases {
		t.Run(tc.Name, func(t *testing.T) {
			sigchecker := NewSignatureChecker(tc.Config)

			got := sigchecker.GetSigningKey()
			if got != tc.Expected {
				t.Errorf("expected=%s, got=%s", tc.Expected, got)
			}
		})
	}
}

func Test_HasSigningKey(t *testing.T) {
	cases := []struct {
		Name     string
		Config   *InstallConfig
		Expected bool
	}{
		{
			Name:     "no signing key",
			Config:   NewInstallConfig(tflint.EmptyConfig(), &tflint.PluginConfig{SigningKey: ""}),
			Expected: false,
		},
		{
			Name:     "configured singing key",
			Config:   NewInstallConfig(tflint.EmptyConfig(), &tflint.PluginConfig{SigningKey: testSigningKey}),
			Expected: true,
		},
		{
			Name:     "bulit-in signing key",
			Config:   NewInstallConfig(tflint.EmptyConfig(), &tflint.PluginConfig{SigningKey: "", SourceOwner: "terraform-linters"}),
			Expected: true,
		},
		{
			Name:     "bulit-in signing key and configured signing key",
			Config:   NewInstallConfig(tflint.EmptyConfig(), &tflint.PluginConfig{SigningKey: testSigningKey, SourceOwner: "terraform-linters"}),
			Expected: true,
		},
	}

	for _, tc := range cases {
		t.Run(tc.Name, func(t *testing.T) {
			sigchecker := NewSignatureChecker(tc.Config)

			got := sigchecker.HasSigningKey()
			if got != tc.Expected {
				t.Errorf("expected=%t, got=%t", tc.Expected, got)
			}
		})
	}
}

func Test_SignatureChecker_Verify(t *testing.T) {
	cwd, err := os.Getwd()
	if err != nil {
		t.Fatal(err)
	}
	signature, err := os.Open(filepath.Join(cwd, "test-fixtures", "signatures", "checksums.txt.sig"))
	if err != nil {
		t.Fatalf("Unexpected error occurred: %s", err)
	}
	defer signature.Close()

	target := `003432556f0380963e6802e701d40a5ea303cfd8ad0cd34719b00c796abfe90e  tflint-ruleset-aws_netbsd_amd64.zip
02caedcd1f0e9c331862b0babce83792d95021977595be9712b01dc0164c488d  tflint-ruleset-aws_netbsd_arm.zip
03ed11c1deeb4dbfc1656b030e4172d1f6b03a1257f219f6265bfc100b20c7a8  tflint-ruleset-aws_netbsd_386.zip
043416079a7ea9e0f7888915278aecda7d6268b61066deacc50feefb6e57836c  tflint-ruleset-aws_linux_arm.zip
30fb5e2d8d8ca3be7247cb0f9e644e3accf0b334d473ce6a5b83151302183e22  tflint-ruleset-aws_openbsd_amd64.zip
3a61fff3689f27c89bce22893219919c629d2e10b96e7eadd5fef9f0e90bb353  tflint-ruleset-aws_darwin_amd64.zip
482419fdeed00692304e59558b5b0d915d4727868b88a5adbbbb76f5ed1b537a  tflint-ruleset-aws_linux_amd64.zip
7147440b689291870ffafd40652a9a623df6c43557da3d2c90e712065e0d093f  tflint-ruleset-aws_freebsd_386.zip
7c7154183f3faf4c80c6703969f5f4903f795eebb881b06da40caa4827eb48e4  tflint-ruleset-aws_windows_386.zip
9df843eb85785246df1e24886b1112b324444dd02329ff45c5cffa597f674b6c  tflint-ruleset-aws_openbsd_arm.zip
b6231a2b94a71841409bb28be0f22eb6ed9de744f0c06ca3a9dd6f80cbc63956  tflint-ruleset-aws_linux_386.zip
bd804df0ff957cda8210c74017211face850cd62445a7d0102493ab5cfdc4276  tflint-ruleset-aws_freebsd_amd64.zip
bee3591d764729769fd32dae7dc8147a890aadc45c00260fa6dafe2df1585a02  tflint-ruleset-aws_openbsd_386.zip
db4eed4c0abcfb0b851da5bbfe8d0c71e1c2b6afe4fd627638a462c655045902  tflint-ruleset-aws_windows_amd64.zip
dd536fed0ebe4c1115240574c5dd7a31b563d67bfe0d1111750438718f995d43  tflint-ruleset-aws_freebsd_arm.zip
`
	reader := strings.NewReader(target)

	sigchecker := NewSignatureChecker(NewInstallConfig(tflint.EmptyConfig(), &tflint.PluginConfig{SigningKey: builtinSigningKey}))
	if err := sigchecker.Verify(reader, signature); err != nil {
		t.Fatalf("Verify failed: %s", err)
	}
}

func Test_SignatureChecker_Verify_errors(t *testing.T) {
	cwd, err := os.Getwd()
	if err != nil {
		t.Fatal(err)
	}
	signature, err := os.Open(filepath.Join(cwd, "test-fixtures", "signatures", "checksums.txt.sig"))
	if err != nil {
		t.Fatalf("Unexpected error occurred: %s", err)
	}
	defer signature.Close()
	brokenSignature := strings.NewReader("broken")

	target := `003432556f0380963e6802e701d40a5ea303cfd8ad0cd34719b00c796abfe90e  tflint-ruleset-aws_netbsd_amd64.zip
02caedcd1f0e9c331862b0babce83792d95021977595be9712b01dc0164c488d  tflint-ruleset-aws_netbsd_arm.zip
03ed11c1deeb4dbfc1656b030e4172d1f6b03a1257f219f6265bfc100b20c7a8  tflint-ruleset-aws_netbsd_386.zip
043416079a7ea9e0f7888915278aecda7d6268b61066deacc50feefb6e57836c  tflint-ruleset-aws_linux_arm.zip
30fb5e2d8d8ca3be7247cb0f9e644e3accf0b334d473ce6a5b83151302183e22  tflint-ruleset-aws_openbsd_amd64.zip
3a61fff3689f27c89bce22893219919c629d2e10b96e7eadd5fef9f0e90bb353  tflint-ruleset-aws_darwin_amd64.zip
482419fdeed00692304e59558b5b0d915d4727868b88a5adbbbb76f5ed1b537a  tflint-ruleset-aws_linux_amd64.zip
7147440b689291870ffafd40652a9a623df6c43557da3d2c90e712065e0d093f  tflint-ruleset-aws_freebsd_386.zip
7c7154183f3faf4c80c6703969f5f4903f795eebb881b06da40caa4827eb48e4  tflint-ruleset-aws_windows_386.zip
9df843eb85785246df1e24886b1112b324444dd02329ff45c5cffa597f674b6c  tflint-ruleset-aws_openbsd_arm.zip
b6231a2b94a71841409bb28be0f22eb6ed9de744f0c06ca3a9dd6f80cbc63956  tflint-ruleset-aws_linux_386.zip
bd804df0ff957cda8210c74017211face850cd62445a7d0102493ab5cfdc4276  tflint-ruleset-aws_freebsd_amd64.zip
bee3591d764729769fd32dae7dc8147a890aadc45c00260fa6dafe2df1585a02  tflint-ruleset-aws_openbsd_386.zip
db4eed4c0abcfb0b851da5bbfe8d0c71e1c2b6afe4fd627638a462c655045902  tflint-ruleset-aws_windows_amd64.zip
dd536fed0ebe4c1115240574c5dd7a31b563d67bfe0d1111750438718f995d43  tflint-ruleset-aws_freebsd_arm.zip
`

	cases := []struct {
		Name      string
		Config    *InstallConfig
		Target    string
		Signature io.Reader
		Expected  error
	}{
		{
			Name:      "invalid signature",
			Config:    NewInstallConfig(tflint.EmptyConfig(), &tflint.PluginConfig{SigningKey: builtinSigningKey}),
			Target:    "broken",
			Signature: signature,
			Expected:  fmt.Errorf("openpgp: invalid signature: hash tag doesn't match"),
		},
		{
			Name:      "no signing key",
			Config:    NewInstallConfig(tflint.EmptyConfig(), &tflint.PluginConfig{SigningKey: ""}),
			Target:    target,
			Signature: signature,
			Expected:  fmt.Errorf("No signing key configured"),
		},
		{
			Name:      "broken signing key",
			Config:    NewInstallConfig(tflint.EmptyConfig(), &tflint.PluginConfig{SigningKey: "broken"}),
			Target:    target,
			Signature: signature,
			Expected:  fmt.Errorf("openpgp: invalid argument: no armored data found"),
		},
		{
			Name:      "broken signature",
			Config:    NewInstallConfig(tflint.EmptyConfig(), &tflint.PluginConfig{SigningKey: builtinSigningKey}),
			Target:    target,
			Signature: brokenSignature,
			Expected:  fmt.Errorf("openpgp: invalid data: tag byte does not have MSB set"),
		},
	}

	for _, tc := range cases {
		t.Run(tc.Name, func(t *testing.T) {
			sigchecker := NewSignatureChecker(tc.Config)
			reader := strings.NewReader(tc.Target)

			err := sigchecker.Verify(reader, tc.Signature)
			if err == nil {
				t.Fatalf("expected=%s, actual=no errors", tc.Expected)
			}
			if err.Error() != tc.Expected.Error() {
				t.Errorf("expected=%s, actual=%s", tc.Expected, err)
			}
		})
	}
}

var testSigningKey string = `
-----BEGIN PGP PUBLIC KEY BLOCK-----


ZF5q4h4I33PSGDdSvGXn9UMY5Isjpg==
=7pIB
-----END PGP PUBLIC KEY BLOCK-----`
