package service

import (
	"context"
	"errors"
	"testing"
	"time"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/stretchr/testify/mock"
	"github.com/stretchr/testify/require"

	coremodel "security-center-backend/internal/model"
	"security-center-backend/internal/modules/caasm/collector"
	"security-center-backend/internal/modules/caasm/mocks"
	caasmmodel "security-center-backend/internal/modules/caasm/model"
)

func TestValidateAWSAccountInputPhase4RejectsRoleAccountMismatch(t *testing.T) {
	account := &caasmmodel.CloudAccount{
		ExternalID: "111111111111",
		Provider:   caasmmodel.CloudProviderAWS,
		Metadata: coremodel.JSONB{
			"role_arn": "arn:aws:iam::222222222222:role/caasm-reader",
			"region":   "us-east-1",
		},
	}

	err := validateAWSAccountInput(account, caasmmodel.ThrottlingConfig{})

	require.Error(t, err)
	require.Contains(t, err.Error(), "must match external_id")
}

func TestValidateAWSAccountIdentityPhase4RejectsAssumedAccountMismatch(t *testing.T) {
	err := validateAWSAccountIdentity(&caasmmodel.CloudAccount{
		ExternalID: "111111111111",
	}, "arn:aws:iam::111111111111:role/caasm-reader", "222222222222", "arn:aws:sts::222222222222:assumed-role/caasm-reader/session")

	var identityErr *awsIdentityValidationError
	require.ErrorAs(t, err, &identityErr)
	require.Equal(t, "111111111111", identityErr.ExpectedAccountID)
	require.Equal(t, "111111111111", identityErr.RoleAccountID)
	require.Equal(t, "222222222222", identityErr.ActualAccountID)
}

func TestValidateAWSAccountIdentityPhase4RejectsEmptyAssumedAccount(t *testing.T) {
	err := validateAWSAccountIdentity(&caasmmodel.CloudAccount{
		ExternalID: "111111111111",
	}, "arn:aws:iam::111111111111:role/caasm-reader", "", "arn:aws:sts::111111111111:assumed-role/caasm-reader/session")

	var identityErr *awsIdentityValidationError
	require.ErrorAs(t, err, &identityErr)
	require.Equal(t, "111111111111", identityErr.ExpectedAccountID)
	require.Empty(t, identityErr.ActualAccountID)
}

func TestBuildDefaultCollectorPhase4RejectsAssumedAccountMismatch(t *testing.T) {
	svc := NewCloudAccountService(nil, nil, nil)
	svc.SetAWSConfigBuilder(func(context.Context, string, string, string) (aws.Config, error) {
		return aws.Config{Region: "us-east-1"}, nil
	})
	svc.SetAWSIdentityVerifier(func(context.Context, aws.Config) (string, string, error) {
		return "222222222222", "arn:aws:sts::222222222222:assumed-role/caasm-reader/session", nil
	})

	_, _, err := svc.buildDefaultCollector(context.Background(), &caasmmodel.CloudAccount{
		Base:       coremodel.Base{ID: 99},
		ExternalID: "111111111111",
		Provider:   caasmmodel.CloudProviderAWS,
		Metadata: coremodel.JSONB{
			"role_arn": "arn:aws:iam::111111111111:role/caasm-reader",
			"region":   "us-east-1",
		},
	})

	var identityErr *awsIdentityValidationError
	require.ErrorAs(t, err, &identityErr)
	require.Equal(t, "222222222222", identityErr.ActualAccountID)
}

func TestValidateAWSPhase4RejectsAssumedAccountMismatch(t *testing.T) {
	svc := NewCloudAccountService(nil, nil, nil)
	svc.SetAWSConfigBuilder(func(context.Context, string, string, string) (aws.Config, error) {
		return aws.Config{Region: "us-east-1"}, nil
	})
	svc.SetAWSIdentityVerifier(func(context.Context, aws.Config) (string, string, error) {
		return "222222222222", "arn:aws:sts::222222222222:assumed-role/caasm-reader/session", nil
	})

	result, err := svc.validateAWS(context.Background(), &caasmmodel.CloudAccount{
		ExternalID: "111111111111",
		Provider:   caasmmodel.CloudProviderAWS,
		Metadata: coremodel.JSONB{
			"role_arn": "arn:aws:iam::111111111111:role/caasm-reader",
			"region":   "us-east-1",
		},
	})

	require.NoError(t, err)
	require.False(t, result.Success)
	require.Contains(t, result.Message, "AWS identity account mismatch")
}

func TestRecordAWSIdentityValidationFailurePhase4RecordsMetadataAndEvent(t *testing.T) {
	jobs := &phase2SyncJobRepo{}
	events := &phase2EventRepo{}
	svc := &CloudAccountService{jobs: jobs, events: events}
	job := &caasmmodel.SyncJob{Base: coremodel.Base{ID: 44}}
	err := &awsIdentityValidationError{
		ExpectedAccountID: "111111111111",
		RoleAccountID:     "111111111111",
		ActualAccountID:   "222222222222",
		ActualARN:         "arn:aws:sts::222222222222:assumed-role/caasm-reader/session",
	}

	svc.recordAWSIdentityValidationFailure(context.Background(), job, err)

	require.True(t, jobs.updateFieldsCalled)
	require.NotNil(t, events.created)
	require.Equal(t, "AWS identity validation failed", events.created.Message)
	require.True(t, job.Metadata["aws_identity_validation_failed"].(bool))
	require.Equal(t, "222222222222", job.Metadata["aws_identity_actual_account_id"])
}

func TestProcessCollectedResultPhase4PartialFailuresBlockPersistAndStaleMarking(t *testing.T) {
	assetRepo := &mocks.MockAssetRepository{}
	jobs := &phase2SyncJobRepo{}
	events := &phase2EventRepo{}
	svc := &CloudAccountService{assets: assetRepo, jobs: jobs, events: events}
	job := &caasmmodel.SyncJob{Base: coremodel.Base{ID: 45}}
	account := &caasmmodel.CloudAccount{Base: coremodel.Base{ID: 99}}

	err := svc.processCollectedResult(context.Background(), job, account, &collector.AssetResult{
		Assets: []caasmmodel.Asset{{
			NativeID: "should-not-persist",
			Name:     "should-not-persist",
		}},
		PartialFailures: []collector.CollectionIssue{{
			Scope:   "region",
			Target:  "us-east-1",
			Message: "failed to collect EKS",
		}},
	}, time.Now())

	var partialErr *collectionPartialFailureError
	require.ErrorAs(t, err, &partialErr)
	require.True(t, jobs.updateFieldsCalled)
	require.NotNil(t, events.created)
	require.Equal(t, "Collection failed with partial failures", events.created.Message)
	require.True(t, job.Metadata["collection_incomplete"].(bool))
	require.True(t, job.Metadata["stale_marking_skipped"].(bool))
	assetRepo.AssertNotCalled(t, "Save", mock.Anything, mock.Anything)
	assetRepo.AssertNotCalled(t, "SoftDeleteStale", mock.Anything, mock.Anything, mock.Anything)
}

func TestHandleCollectionPartialFailuresPhase4ReturnsMetadataUpdateError(t *testing.T) {
	jobs := &phase4SyncJobRepo{updateErr: errors.New("metadata db down")}
	events := &phase2EventRepo{}
	svc := &CloudAccountService{jobs: jobs, events: events}
	job := &caasmmodel.SyncJob{Base: coremodel.Base{ID: 46}}

	err := svc.handleCollectionPartialFailures(context.Background(), job, &collector.AssetResult{
		PartialFailures: []collector.CollectionIssue{{
			Scope:   "global",
			Target:  "route53",
			Message: "denied",
		}},
	})

	require.Error(t, err)
	require.Contains(t, err.Error(), "metadata db down")
	require.NotNil(t, events.created)
	require.Equal(t, "metadata db down", events.created.Metadata["metadata_update_error"])
}

type phase4SyncJobRepo struct {
	phase2SyncJobRepo
	updateErr error
}

func (r *phase4SyncJobRepo) UpdateFields(ctx context.Context, id uint64, fields map[string]interface{}) error {
	r.updateFieldsCalled = true
	r.updatedFields = fields
	r.updateCtxErr = ctx.Err()
	return r.updateErr
}
