package service

import (
	"context"
	"testing"

	"github.com/stretchr/testify/require"

	vulnmodel "security-center-backend/internal/modules/vuln/model"
)

type stubSubmissionRequiredFieldRepo struct {
	fields   []string
	replaced []string
}

func (r *stubSubmissionRequiredFieldRepo) List(ctx context.Context) ([]string, error) {
	return append([]string{}, r.fields...), nil
}

func (r *stubSubmissionRequiredFieldRepo) Replace(ctx context.Context, fields []string) error {
	r.replaced = append([]string{}, fields...)
	r.fields = append([]string{}, fields...)
	return nil
}

func TestGetConfigurationIncludesDefaultSubmissionRequiredFields(t *testing.T) {
	svc := New(nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)

	config, err := svc.GetConfiguration(context.Background())

	require.NoError(t, err)
	require.Equal(t, DefaultSubmissionRequiredFields(), config.SubmissionRequiredFields)
}

func TestUpdateSubmissionRequiredFieldsRejectsUnknownField(t *testing.T) {
	svc := New(nil, nil, nil, nil, nil, nil, nil, nil, &stubSubmissionRequiredFieldRepo{}, nil, nil)

	err := svc.UpdateSubmissionRequiredFields(context.Background(), []string{"application", "not_a_field"})

	require.ErrorContains(t, err, "unknown submission required field: not_a_field")
}

func TestUpdateSubmissionRequiredFieldsCannotRemoveLockedFields(t *testing.T) {
	repo := &stubSubmissionRequiredFieldRepo{}
	svc := New(nil, nil, nil, nil, nil, nil, nil, nil, repo, nil, nil)

	err := svc.UpdateSubmissionRequiredFields(context.Background(), []string{"application"})

	require.NoError(t, err)
	require.ElementsMatch(t, []string{
		SubmissionFieldTitle,
		SubmissionFieldSeverityLevel,
		SubmissionFieldDiscovererEmployeeID,
		SubmissionFieldApplication,
	}, repo.replaced)
}

func TestUpdateSubmissionRequiredFieldsRejectsSystemGeneratedDates(t *testing.T) {
	svc := New(nil, nil, nil, nil, nil, nil, nil, nil, &stubSubmissionRequiredFieldRepo{}, nil, nil)

	err := svc.UpdateSubmissionRequiredFields(context.Background(), []string{"submitted_at", "sla_deadline"})

	require.ErrorContains(t, err, "unknown submission required field: submitted_at")
}

func TestValidateSubmissionRequiredFieldsFailsForConfiguredMissingField(t *testing.T) {
	repo := &stubSubmissionRequiredFieldRepo{
		fields: []string{
			SubmissionFieldTitle,
			SubmissionFieldSeverityLevel,
			SubmissionFieldDiscovererEmployeeID,
			SubmissionFieldApplication,
		},
	}
	svc := New(nil, nil, nil, nil, nil, nil, nil, nil, repo, nil, nil)
	employeeID := uint64(10)

	err := svc.ValidateSubmissionRequiredFields(context.Background(), SubmissionRequiredFieldValues{
		Title:                "SQL injection",
		SeverityLevel:        vulnmodel.SeverityHigh,
		DiscovererEmployeeID: &employeeID,
	})

	require.ErrorContains(t, err, "application is required")
}

func TestValidateSubmissionRequiredFieldsAllowsOptionalApplication(t *testing.T) {
	repo := &stubSubmissionRequiredFieldRepo{
		fields: []string{
			SubmissionFieldTitle,
			SubmissionFieldSeverityLevel,
			SubmissionFieldDiscovererEmployeeID,
		},
	}
	svc := New(nil, nil, nil, nil, nil, nil, nil, nil, repo, nil, nil)
	employeeID := uint64(10)

	err := svc.ValidateSubmissionRequiredFields(context.Background(), SubmissionRequiredFieldValues{
		Title:                "SQL injection",
		SeverityLevel:        vulnmodel.SeverityHigh,
		DiscovererEmployeeID: &employeeID,
	})

	require.NoError(t, err)
}

func TestValidateSubmissionRequiredFieldsTreatsBlankRichTextDescriptionAsMissing(t *testing.T) {
	repo := &stubSubmissionRequiredFieldRepo{
		fields: []string{
			SubmissionFieldTitle,
			SubmissionFieldSeverityLevel,
			SubmissionFieldDiscovererEmployeeID,
			SubmissionFieldDescription,
		},
	}
	svc := New(nil, nil, nil, nil, nil, nil, nil, nil, repo, nil, nil)
	employeeID := uint64(10)

	err := svc.ValidateSubmissionRequiredFields(context.Background(), SubmissionRequiredFieldValues{
		Title:                "SQL injection",
		SeverityLevel:        vulnmodel.SeverityHigh,
		DiscovererEmployeeID: &employeeID,
		Description:          "<p><br></p>",
	})

	require.ErrorContains(t, err, "description is required")
}

func TestValidateSubmissionRequiredFieldsAllowsAttachmentOnlyDescription(t *testing.T) {
	repo := &stubSubmissionRequiredFieldRepo{
		fields: []string{
			SubmissionFieldTitle,
			SubmissionFieldSeverityLevel,
			SubmissionFieldDiscovererEmployeeID,
			SubmissionFieldDescription,
		},
	}
	svc := New(nil, nil, nil, nil, nil, nil, nil, nil, repo, nil, nil)
	employeeID := uint64(10)

	err := svc.ValidateSubmissionRequiredFields(context.Background(), SubmissionRequiredFieldValues{
		Title:                "SQL injection",
		SeverityLevel:        vulnmodel.SeverityHigh,
		DiscovererEmployeeID: &employeeID,
		Description:          `<p><img data-sc-attachment-id="55"></p>`,
	})

	require.NoError(t, err)
}
