// Copyright 2022 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package github

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"testing"
	"time"

	"github.com/google/go-cmp/cmp"
)

func TestSecretScanningService_ListAlertsForEnterprise(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/enterprises/e/secret-scanning/alerts", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		testFormValues(t, r, values{"state": "open", "secret_type": "mailchimp_api_key"})

		fmt.Fprint(w, `[{
			"number": 1,
			"created_at": "1996-06-20T00:00:00Z",
			"url": "https://api.github.com/repos/o/r/secret-scanning/alerts/1",
			"html_url": "https://github.com/o/r/security/secret-scanning/1",
			"locations_url": "https://api.github.com/repos/o/r/secret-scanning/alerts/1/locations",
			"state": "open",
			"resolution": null,
			"resolved_at": null,
			"resolved_by": null,
			"secret_type": "mailchimp_api_key",
			"secret": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-us2",
			"repository": {
				"id": 1,
				"name": "n",
				"url": "url"
			}
		}]`)
	})

	ctx := context.Background()
	opts := &SecretScanningAlertListOptions{State: "open", SecretType: "mailchimp_api_key"}

	alerts, _, err := client.SecretScanning.ListAlertsForEnterprise(ctx, "e", opts)
	if err != nil {
		t.Errorf("SecretScanning.ListAlertsForEnterprise returned error: %v", err)
	}

	date := Timestamp{time.Date(1996, time.June, 20, 00, 00, 00, 0, time.UTC)}
	want := []*SecretScanningAlert{
		{
			Number:       Int(1),
			CreatedAt:    &date,
			URL:          String("https://api.github.com/repos/o/r/secret-scanning/alerts/1"),
			HTMLURL:      String("https://github.com/o/r/security/secret-scanning/1"),
			LocationsURL: String("https://api.github.com/repos/o/r/secret-scanning/alerts/1/locations"),
			State:        String("open"),
			Resolution:   nil,
			ResolvedAt:   nil,
			ResolvedBy:   nil,
			SecretType:   String("mailchimp_api_key"),
			Secret:       String("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-us2"),
			Repository: &Repository{
				ID:   Int64(1),
				URL:  String("url"),
				Name: String("n"),
			},
		},
	}

	if !cmp.Equal(alerts, want) {
		t.Errorf("SecretScanning.ListAlertsForEnterprise returned %+v, want %+v", alerts, want)
	}

	const methodName = "ListAlertsForEnterprise"

	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.SecretScanning.ListAlertsForEnterprise(ctx, "\n", opts)
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		_, resp, err := client.SecretScanning.ListAlertsForEnterprise(ctx, "e", opts)
		return resp, err
	})
}

func TestSecretScanningService_ListAlertsForOrg(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/orgs/o/secret-scanning/alerts", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		testFormValues(t, r, values{"state": "open", "secret_type": "mailchimp_api_key"})

		fmt.Fprint(w, `[{
			"number": 1,
			"created_at": "1996-06-20T00:00:00Z",
			"url": "https://api.github.com/repos/o/r/secret-scanning/alerts/1",
			"html_url": "https://github.com/o/r/security/secret-scanning/1",
			"locations_url": "https://api.github.com/repos/o/r/secret-scanning/alerts/1/locations",
			"state": "open",
			"resolution": null,
			"resolved_at": null,
			"resolved_by": null,
			"secret_type": "mailchimp_api_key",
			"secret": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-us2"
		}]`)
	})

	ctx := context.Background()
	opts := &SecretScanningAlertListOptions{State: "open", SecretType: "mailchimp_api_key"}

	alerts, _, err := client.SecretScanning.ListAlertsForOrg(ctx, "o", opts)
	if err != nil {
		t.Errorf("SecretScanning.ListAlertsForOrg returned error: %v", err)
	}

	date := Timestamp{time.Date(1996, time.June, 20, 00, 00, 00, 0, time.UTC)}
	want := []*SecretScanningAlert{
		{
			Number:       Int(1),
			CreatedAt:    &date,
			URL:          String("https://api.github.com/repos/o/r/secret-scanning/alerts/1"),
			HTMLURL:      String("https://github.com/o/r/security/secret-scanning/1"),
			LocationsURL: String("https://api.github.com/repos/o/r/secret-scanning/alerts/1/locations"),
			State:        String("open"),
			Resolution:   nil,
			ResolvedAt:   nil,
			ResolvedBy:   nil,
			SecretType:   String("mailchimp_api_key"),
			Secret:       String("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-us2"),
		},
	}

	if !cmp.Equal(alerts, want) {
		t.Errorf("SecretScanning.ListAlertsForOrg returned %+v, want %+v", alerts, want)
	}

	const methodName = "ListAlertsForOrg"

	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.SecretScanning.ListAlertsForOrg(ctx, "\n", opts)
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		_, resp, err := client.SecretScanning.ListAlertsForOrg(ctx, "o", opts)
		return resp, err
	})
}

func TestSecretScanningService_ListAlertsForOrgListOptions(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/orgs/o/secret-scanning/alerts", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		testFormValues(t, r, values{"state": "open", "secret_type": "mailchimp_api_key", "per_page": "1", "page": "1"})

		fmt.Fprint(w, `[{
			"number": 1,
			"created_at": "1996-06-20T00:00:00Z",
			"url": "https://api.github.com/repos/o/r/secret-scanning/alerts/1",
			"html_url": "https://github.com/o/r/security/secret-scanning/1",
			"locations_url": "https://api.github.com/repos/o/r/secret-scanning/alerts/1/locations",
			"state": "open",
			"resolution": null,
			"resolved_at": null,
			"resolved_by": null,
			"secret_type": "mailchimp_api_key",
			"secret": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-us2"
		}]`)
	})

	ctx := context.Background()

	// Testing pagination by index
	opts := &SecretScanningAlertListOptions{State: "open", SecretType: "mailchimp_api_key", ListOptions: ListOptions{Page: 1, PerPage: 1}}

	alerts, _, err := client.SecretScanning.ListAlertsForOrg(ctx, "o", opts)
	if err != nil {
		t.Errorf("SecretScanning.ListAlertsForOrg returned error: %v", err)
	}

	date := Timestamp{time.Date(1996, time.June, 20, 00, 00, 00, 0, time.UTC)}
	want := []*SecretScanningAlert{
		{
			Number:       Int(1),
			CreatedAt:    &date,
			URL:          String("https://api.github.com/repos/o/r/secret-scanning/alerts/1"),
			HTMLURL:      String("https://github.com/o/r/security/secret-scanning/1"),
			LocationsURL: String("https://api.github.com/repos/o/r/secret-scanning/alerts/1/locations"),
			State:        String("open"),
			Resolution:   nil,
			ResolvedAt:   nil,
			ResolvedBy:   nil,
			SecretType:   String("mailchimp_api_key"),
			Secret:       String("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-us2"),
		},
	}

	if !cmp.Equal(alerts, want) {
		t.Errorf("SecretScanning.ListAlertsForOrg returned %+v, want %+v", alerts, want)
	}

	const methodName = "ListAlertsForOrg"

	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.SecretScanning.ListAlertsForOrg(ctx, "\n", opts)
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		_, resp, err := client.SecretScanning.ListAlertsForOrg(ctx, "o", opts)
		return resp, err
	})
}

func TestSecretScanningService_ListAlertsForRepo(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/repos/o/r/secret-scanning/alerts", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		testFormValues(t, r, values{"state": "open", "secret_type": "mailchimp_api_key"})

		fmt.Fprint(w, `[{
			"number": 1,
			"created_at": "1996-06-20T00:00:00Z",
			"url": "https://api.github.com/repos/o/r/secret-scanning/alerts/1",
			"html_url": "https://github.com/o/r/security/secret-scanning/1",
			"locations_url": "https://api.github.com/repos/o/r/secret-scanning/alerts/1/locations",
			"state": "open",
			"resolution": null,
			"resolved_at": null,
			"resolved_by": null,
			"secret_type": "mailchimp_api_key",
			"secret": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-us2"
		}]`)
	})

	ctx := context.Background()
	opts := &SecretScanningAlertListOptions{State: "open", SecretType: "mailchimp_api_key"}

	alerts, _, err := client.SecretScanning.ListAlertsForRepo(ctx, "o", "r", opts)
	if err != nil {
		t.Errorf("SecretScanning.ListAlertsForRepo returned error: %v", err)
	}

	date := Timestamp{time.Date(1996, time.June, 20, 00, 00, 00, 0, time.UTC)}
	want := []*SecretScanningAlert{
		{
			Number:       Int(1),
			CreatedAt:    &date,
			URL:          String("https://api.github.com/repos/o/r/secret-scanning/alerts/1"),
			HTMLURL:      String("https://github.com/o/r/security/secret-scanning/1"),
			LocationsURL: String("https://api.github.com/repos/o/r/secret-scanning/alerts/1/locations"),
			State:        String("open"),
			Resolution:   nil,
			ResolvedAt:   nil,
			ResolvedBy:   nil,
			SecretType:   String("mailchimp_api_key"),
			Secret:       String("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-us2"),
		},
	}

	if !cmp.Equal(alerts, want) {
		t.Errorf("SecretScanning.ListAlertsForRepo returned %+v, want %+v", alerts, want)
	}

	const methodName = "ListAlertsForRepo"

	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.SecretScanning.ListAlertsForRepo(ctx, "\n", "\n", opts)
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		_, resp, err := client.SecretScanning.ListAlertsForRepo(ctx, "o", "r", opts)
		return resp, err
	})
}

func TestSecretScanningService_GetAlert(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/repos/o/r/secret-scanning/alerts/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")

		fmt.Fprint(w, `{
			"number": 1,
			"created_at": "1996-06-20T00:00:00Z",
			"url": "https://api.github.com/repos/o/r/secret-scanning/alerts/1",
			"html_url": "https://github.com/o/r/security/secret-scanning/1",
			"locations_url": "https://api.github.com/repos/o/r/secret-scanning/alerts/1/locations",
			"state": "open",
			"resolution": null,
			"resolved_at": null,
			"resolved_by": null,
			"secret_type": "mailchimp_api_key",
			"secret": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-us2"
		}`)
	})

	ctx := context.Background()

	alert, _, err := client.SecretScanning.GetAlert(ctx, "o", "r", 1)
	if err != nil {
		t.Errorf("SecretScanning.GetAlert returned error: %v", err)
	}

	date := Timestamp{time.Date(1996, time.June, 20, 00, 00, 00, 0, time.UTC)}
	want := &SecretScanningAlert{
		Number:       Int(1),
		CreatedAt:    &date,
		URL:          String("https://api.github.com/repos/o/r/secret-scanning/alerts/1"),
		HTMLURL:      String("https://github.com/o/r/security/secret-scanning/1"),
		LocationsURL: String("https://api.github.com/repos/o/r/secret-scanning/alerts/1/locations"),
		State:        String("open"),
		Resolution:   nil,
		ResolvedAt:   nil,
		ResolvedBy:   nil,
		SecretType:   String("mailchimp_api_key"),
		Secret:       String("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-us2"),
	}

	if !cmp.Equal(alert, want) {
		t.Errorf("SecretScanning.GetAlert returned %+v, want %+v", alert, want)
	}

	const methodName = "GetAlert"

	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.SecretScanning.GetAlert(ctx, "\n", "\n", 0)
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		_, resp, err := client.SecretScanning.GetAlert(ctx, "o", "r", 1)
		return resp, err
	})
}

func TestSecretScanningService_UpdateAlert(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/repos/o/r/secret-scanning/alerts/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "PATCH")

		v := new(SecretScanningAlertUpdateOptions)
		assertNilError(t, json.NewDecoder(r.Body).Decode(v))

		want := &SecretScanningAlertUpdateOptions{State: "resolved", Resolution: String("used_in_tests")}

		if !cmp.Equal(v, want) {
			t.Errorf("Request body = %+v, want %+v", v, want)
		}

		fmt.Fprint(w, `{
			"number": 1,
			"created_at": "1996-06-20T00:00:00Z",
			"url": "https://api.github.com/repos/o/r/secret-scanning/alerts/1",
			"html_url": "https://github.com/o/r/security/secret-scanning/1",
			"locations_url": "https://api.github.com/repos/o/r/secret-scanning/alerts/1/locations",
			"state": "resolved",
			"resolution": "used_in_tests",
			"resolved_at": "1996-06-20T00:00:00Z",
			"resolved_by": null,
			"secret_type": "mailchimp_api_key",
			"secret": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-us2"
		}`)
	})

	ctx := context.Background()
	opts := &SecretScanningAlertUpdateOptions{State: "resolved", Resolution: String("used_in_tests")}

	alert, _, err := client.SecretScanning.UpdateAlert(ctx, "o", "r", 1, opts)
	if err != nil {
		t.Errorf("SecretScanning.UpdateAlert returned error: %v", err)
	}

	date := Timestamp{time.Date(1996, time.June, 20, 00, 00, 00, 0, time.UTC)}
	want := &SecretScanningAlert{
		Number:       Int(1),
		CreatedAt:    &date,
		URL:          String("https://api.github.com/repos/o/r/secret-scanning/alerts/1"),
		HTMLURL:      String("https://github.com/o/r/security/secret-scanning/1"),
		LocationsURL: String("https://api.github.com/repos/o/r/secret-scanning/alerts/1/locations"),
		State:        String("resolved"),
		Resolution:   String("used_in_tests"),
		ResolvedAt:   &date,
		ResolvedBy:   nil,
		SecretType:   String("mailchimp_api_key"),
		Secret:       String("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-us2"),
	}

	if !cmp.Equal(alert, want) {
		t.Errorf("SecretScanning.UpdateAlert returned %+v, want %+v", alert, want)
	}

	const methodName = "UpdateAlert"

	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.SecretScanning.UpdateAlert(ctx, "\n", "\n", 1, opts)
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		_, resp, err := client.SecretScanning.UpdateAlert(ctx, "o", "r", 1, opts)
		return resp, err
	})
}

func TestSecretScanningService_ListLocationsForAlert(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/repos/o/r/secret-scanning/alerts/1/locations", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		testFormValues(t, r, values{"page": "1", "per_page": "100"})

		fmt.Fprint(w, `[{
			"type": "commit",
			"details": {
			  "path": "/example/secrets.txt",
			  "start_line": 1,
			  "end_line": 1,
			  "start_column": 1,
			  "end_column": 64,
			  "blob_sha": "af5626b4a114abcb82d63db7c8082c3c4756e51b",
			  "blob_url": "https://api.github.com/repos/o/r/git/blobs/af5626b4a114abcb82d63db7c8082c3c4756e51b",
			  "commit_sha": "f14d7debf9775f957cf4f1e8176da0786431f72b",
			  "commit_url": "https://api.github.com/repos/o/r/git/commits/f14d7debf9775f957cf4f1e8176da0786431f72b"
			}
		}]`)
	})

	ctx := context.Background()
	opts := &ListOptions{Page: 1, PerPage: 100}

	locations, _, err := client.SecretScanning.ListLocationsForAlert(ctx, "o", "r", 1, opts)
	if err != nil {
		t.Errorf("SecretScanning.ListLocationsForAlert returned error: %v", err)
	}

	want := []*SecretScanningAlertLocation{
		{
			Type: String("commit"),
			Details: &SecretScanningAlertLocationDetails{
				Path:        String("/example/secrets.txt"),
				Startline:   Int(1),
				EndLine:     Int(1),
				StartColumn: Int(1),
				EndColumn:   Int(64),
				BlobSHA:     String("af5626b4a114abcb82d63db7c8082c3c4756e51b"),
				BlobURL:     String("https://api.github.com/repos/o/r/git/blobs/af5626b4a114abcb82d63db7c8082c3c4756e51b"),
				CommitSHA:   String("f14d7debf9775f957cf4f1e8176da0786431f72b"),
				CommitURL:   String("https://api.github.com/repos/o/r/git/commits/f14d7debf9775f957cf4f1e8176da0786431f72b"),
			},
		},
	}

	if !cmp.Equal(locations, want) {
		t.Errorf("SecretScanning.ListLocationsForAlert returned %+v, want %+v", locations, want)
	}

	const methodName = "ListLocationsForAlert"

	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.SecretScanning.ListLocationsForAlert(ctx, "\n", "\n", 1, opts)
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		_, resp, err := client.SecretScanning.ListLocationsForAlert(ctx, "o", "r", 1, opts)
		return resp, err
	})
}

func TestSecretScanningAlert_Marshal(t *testing.T) {
	testJSONMarshal(t, &SecretScanningAlert{}, `{}`)

	u := &SecretScanningAlert{
		Number:       Int(1),
		CreatedAt:    &Timestamp{referenceTime},
		URL:          String("https://api.github.com/teams/2/discussions/3/comments"),
		HTMLURL:      String("https://api.github.com/teams/2/discussions/3/comments"),
		LocationsURL: String("https://api.github.com/teams/2/discussions/3/comments"),
		State:        String("test_state"),
		Resolution:   String("test_resolution"),
		ResolvedAt:   &Timestamp{referenceTime},
		ResolvedBy: &User{
			Login:     String("test"),
			ID:        Int64(10),
			NodeID:    String("A123"),
			AvatarURL: String("https://api.github.com/teams/2/discussions/3/comments"),
		},
		SecretType: String("test"),
		Secret:     String("test"),
	}

	want := `{
		"number": 1,
		"created_at": ` + referenceTimeStr + `,
		"url": "https://api.github.com/teams/2/discussions/3/comments",
		"html_url": "https://api.github.com/teams/2/discussions/3/comments",
		"locations_url": "https://api.github.com/teams/2/discussions/3/comments",
		"state": "test_state",
		"resolution": "test_resolution",
		"resolved_at": ` + referenceTimeStr + `,
		"resolved_by": {
			"login": "test",
			"id": 10,
			"node_id": "A123",
			"avatar_url": "https://api.github.com/teams/2/discussions/3/comments"
		},
		"secret_type": "test",
		"secret": "test"
	}`

	testJSONMarshal(t, u, want)
}

func TestSecretScanningAlertLocation_Marshal(t *testing.T) {
	testJSONMarshal(t, &SecretScanningAlertLocation{}, `{}`)

	u := &SecretScanningAlertLocation{
		Type: String("test"),
		Details: &SecretScanningAlertLocationDetails{
			Path:        String("test_path"),
			Startline:   Int(10),
			EndLine:     Int(20),
			StartColumn: Int(30),
			EndColumn:   Int(40),
			BlobSHA:     String("test_sha"),
			BlobURL:     String("https://api.github.com/repos/o/r/git/commits/f14d7debf9775f957cf4f1e8176da0786431f72b"),
			CommitSHA:   String("test_sha"),
			CommitURL:   String("https://api.github.com/repos/o/r/git/commits/f14d7debf9775f957cf4f1e8176da0786431f72b"),
		},
	}

	want := `{
		"type": "test",
		"details": {
			"path": "test_path",
			"start_line": 10,
			"end_line": 20,
			"start_column": 30,
			"end_column": 40,
			"blob_sha": "test_sha",
			"blob_url": "https://api.github.com/repos/o/r/git/commits/f14d7debf9775f957cf4f1e8176da0786431f72b",
			"commit_sha": "test_sha",
			"commit_url": "https://api.github.com/repos/o/r/git/commits/f14d7debf9775f957cf4f1e8176da0786431f72b"
		} 
	}`

	testJSONMarshal(t, u, want)
}

func TestSecretScanningAlertLocationDetails_Marshal(t *testing.T) {
	testJSONMarshal(t, &SecretScanningAlertLocationDetails{}, `{}`)

	u := &SecretScanningAlertLocationDetails{
		Path:        String("test_path"),
		Startline:   Int(10),
		EndLine:     Int(20),
		StartColumn: Int(30),
		EndColumn:   Int(40),
		BlobSHA:     String("test_sha"),
		BlobURL:     String("https://api.github.com/repos/o/r/git/commits/f14d7debf9775f957cf4f1e8176da0786431f72b"),
		CommitSHA:   String("test_sha"),
		CommitURL:   String("https://api.github.com/repos/o/r/git/commits/f14d7debf9775f957cf4f1e8176da0786431f72b"),
	}

	want := `{
		"path": "test_path",
		"start_line": 10,
		"end_line": 20,
		"start_column": 30,
		"end_column": 40,
		"blob_sha": "test_sha",
		"blob_url": "https://api.github.com/repos/o/r/git/commits/f14d7debf9775f957cf4f1e8176da0786431f72b",
		"commit_sha": "test_sha",
		"commit_url": "https://api.github.com/repos/o/r/git/commits/f14d7debf9775f957cf4f1e8176da0786431f72b"	
	}`

	testJSONMarshal(t, u, want)
}

func TestSecretScanningAlertUpdateOptions_Marshal(t *testing.T) {
	testJSONMarshal(t, &SecretScanningAlertUpdateOptions{}, `{}`)

	u := &SecretScanningAlertUpdateOptions{
		State:      "open",
		Resolution: String("false_positive"),
	}

	want := `{
		"state": "open",
		"resolution": "false_positive"
	}`

	testJSONMarshal(t, u, want)
}
