package sast_result_remediation

import (
	"fmt"
	"reflect"
	"testing"
)

const expectedOutputFormat = "<span style=\"color: regular;\">" + boldConfidence + "</span><span style=\"color: grey; font-style: italic;\"> " +
	"A score between 0 (low) and 100 (high) indicating the degree of confidence in the exploitability of this vulnerability in the context of your code. " +
	"<br></span>%s<span style=\"color: regular;\">" + boldExplanation + "</span><span style=\"color: grey; font-style: italic;\"> " +
	"An OpenAI generated description of the vulnerability. <br></span>%s<span style=\"color: " +
	"regular;\">" + boldFix + "</span><span style=\"color: grey; font-style: italic;\"> " +
	"A customized snippet, generated by OpenAI, that can be used to remediate the vulnerability in your code. <br></span>%s"

const (
	boldConfidence2  = bold + confidence + bold2
	boldExplanation2 = bold + explanation + bold2
	boldFix2         = bold + fix + bold2
	boldConfidence3  = bold + confidence
	boldExplanation3 = bold + explanation
	boldFix3         = bold + fix
	boldFix4         = bold + "PROPOSED  REMEDIATION" + bold2
	plainConfidence  = confidence
	plainExplanation = explanation
	plainFix         = fix
	header           = "##"
	mixedFix         = "PROPOSED  REMEdiation"
	explanationText  = " this is a short text explaining the score."
	fixText          = " a fixed snippet"
)

func getExpectedOutput(confidenceNumber, explanationText, fixText string) string {
	return fmt.Sprintf(expectedOutputFormat, confidenceNumber, explanationText, fixText)
}

func TestAddDescriptionForIdentifiers(t *testing.T) {
	input := boldConfidence + " 35 " + boldExplanation + explanationText + boldFix + fixText
	expected := getExpectedOutput(" 35 ", explanationText, fixText)
	output := getActual(input, t)

	if output[len(output)-1] != expected {
		t.Errorf("Expected %q, but got %q", expected, output)
	}
}

func TestAddNewlinesIfNecessarySomeNewlines(t *testing.T) {
	input := boldConfidence + " 35 " + boldExplanation + explanationText + "\n" + boldFix + fixText
	expected := getExpectedOutput(" 35 ", explanationText+"\n", fixText)

	output := getActual(input, t)

	if output[len(output)-1] != expected {
		t.Errorf("Expected %q, but got %q", expected, output)
	}
}

func TestAddNewlinesIfNecessaryAllNewlines(t *testing.T) {
	input := boldConfidence + " 35\n " + boldExplanation + explanationText + "\n" + boldFix + fixText
	expected := getExpectedOutput(" 35\n ", explanationText+"\n", fixText)

	output := getActual(input, t)

	if output[len(output)-1] != expected {
		t.Errorf("Expected %q, but got %q", expected, output)
	}
}

func TestParseResponse(t *testing.T) {
	introText := "this is some introductory text"
	goodConfidenceText := " 35\n"
	goodConfidenceText3 := " 35**\n"
	badConfidenceText := "0\nfailed0"
	badConfidenceText2 := "N/A"
	confidenceValue := 35
	explanationText := explanationText + "\n"

	tests := []struct {
		name     string
		input    string
		expected *ParsedResponse
		err      error
	}{
		{"TestParseResponseHappy", introText + boldConfidence + goodConfidenceText + boldExplanation + explanationText + boldFix + fixText,
			&ParsedResponse{Introduction: introText, Confidence: confidenceValue, Explanation: explanationText, Fix: fixText}, nil},
		{"TestParseResponseHappy2", introText + boldConfidence2 + goodConfidenceText + boldExplanation + explanationText + boldFix + fixText,
			&ParsedResponse{Introduction: introText, Confidence: confidenceValue, Explanation: explanationText, Fix: fixText}, nil},
		{"TestParseResponseHappy3", introText + boldConfidence3 + goodConfidenceText3 + boldExplanation + explanationText + boldFix + fixText,
			&ParsedResponse{Introduction: introText, Confidence: confidenceValue, Explanation: explanationText, Fix: fixText}, nil},
		{"TestParseResponseHappy4", introText + boldConfidence + goodConfidenceText + boldExplanation2 + explanationText + boldFix2 + fixText,
			&ParsedResponse{Introduction: introText, Confidence: confidenceValue, Explanation: explanationText, Fix: fixText}, nil},
		{"TestParseResponseHappy5", introText + boldConfidence + goodConfidenceText + boldExplanation3 + explanationText + boldFix3 + fixText,
			&ParsedResponse{Introduction: introText, Confidence: confidenceValue, Explanation: explanationText, Fix: fixText}, nil},
		{"TestParseResponseHappy6", introText + boldConfidence + goodConfidenceText + boldExplanation3 + explanationText + boldFix4 + fixText,
			&ParsedResponse{Introduction: introText, Confidence: confidenceValue, Explanation: explanationText, Fix: fixText}, nil},
		{"TestParseResponseHappy7", introText + header + plainConfidence + goodConfidenceText + header + plainExplanation + explanationText + header + plainFix + fixText,
			&ParsedResponse{Introduction: introText + header, Confidence: confidenceValue, Explanation: explanationText + header, Fix: fixText}, nil},
		{"TestParseResponseHappy8", introText + boldConfidence + goodConfidenceText + boldExplanation + explanationText + mixedFix + fixText,
			&ParsedResponse{Introduction: introText, Confidence: confidenceValue, Explanation: explanationText, Fix: fixText}, nil},
		{"TestParseResponseNoConfidence", introText + goodConfidenceText + boldExplanation + explanationText + boldFix + fixText,
			&ParsedResponse{Introduction: "", Confidence: 0, Explanation: "", Fix: ""},
			fmt.Errorf("confidence not found in response")},
		{"TestParseResponseNoExplanation", introText + boldConfidence + goodConfidenceText + explanationText + boldFix + fixText,
			&ParsedResponse{Introduction: introText, Confidence: 0, Explanation: "", Fix: ""},
			fmt.Errorf("explanation not found in response")},
		{"TestParseResponseNoFix", introText + boldConfidence + goodConfidenceText + boldExplanation + explanationText + fixText,
			&ParsedResponse{Introduction: introText, Confidence: 0, Explanation: "", Fix: ""},
			fmt.Errorf("fix not found in response")},
		{"TestParseResponseBadConfidenceValue", introText + boldConfidence + badConfidenceText + boldExplanation + explanationText + boldFix + fixText,
			&ParsedResponse{Introduction: introText, Confidence: 0, Explanation: explanationText, Fix: fixText},
			fmt.Errorf("error converting confidence text to integer value: EOF")},
		{"TestParseResponseBadConfidenceValue", introText + boldConfidence + badConfidenceText2 + boldExplanation + explanationText + boldFix + fixText,
			&ParsedResponse{Introduction: introText, Confidence: 0, Explanation: explanationText, Fix: fixText},
			fmt.Errorf("error converting confidence text to integer value: EOF")},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			actual, err := ParseResponse(tt.input)
			if err != nil &&
				(err.Error() != tt.err.Error()) {
				t.Errorf("ParseResponse() gotErr = \n%v\nwantErr \n%v\n", err, tt.err)
			}

			if !reflect.DeepEqual(*actual, *tt.expected) {
				t.Errorf("Expected %q, but got %q", *tt.expected, *actual)
			}
		})
	}
}

func getActual(input string, t *testing.T) []string {
	someText := "some text"
	response := []string{someText, someText, input}
	output := AddDescriptionForIdentifier(response)
	for i := 0; i < len(output)-1; i++ {
		if output[i] != response[i] {
			t.Errorf("All strings except last expected to stay the same")
		}
	}
	return output
}
