package service

import (
	"context"
	"testing"
	"time"

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

func TestPersistSyncJobSuccessIfActiveDoesNotOverwriteTerminalState(t *testing.T) {
	repo := &phase2SyncJobRepo{
		job: &caasmmodel.SyncJob{
			Status:       caasmmodel.SyncJobFailed,
			ErrorMessage: "zombie reaper marked failed",
		},
	}
	svc := &CloudAccountService{jobs: repo}

	if svc.persistSyncJobSuccessIfActive(context.Background(), &caasmmodel.SyncJob{Base: coremodel.Base{ID: 42}}, time.Now()) {
		t.Fatal("expected success terminal update to be skipped")
	}
	if repo.updateFieldsCalled {
		t.Fatal("success update should not run for externally terminal job")
	}
}

func TestPersistSyncJobSuccessIfActiveUpdatesActiveStateWithDurableContext(t *testing.T) {
	repo := &phase2SyncJobRepo{
		job: &caasmmodel.SyncJob{Status: caasmmodel.SyncJobRunning},
	}
	svc := &CloudAccountService{jobs: repo}
	ctx, cancel := context.WithCancel(context.Background())
	cancel()

	if !svc.persistSyncJobSuccessIfActive(ctx, &caasmmodel.SyncJob{Base: coremodel.Base{ID: 42}}, time.Now()) {
		t.Fatal("expected success terminal update to persist for active job")
	}
	if got := repo.updatedFields["status"]; got != caasmmodel.SyncJobSuccess {
		t.Fatalf("updated status = %v, want %v", got, caasmmodel.SyncJobSuccess)
	}
	if repo.updateCtxErr != nil {
		t.Fatalf("update used canceled context: %v", repo.updateCtxErr)
	}
}

func TestPersistSyncJobFailureIfActiveDoesNotOverwriteTerminalState(t *testing.T) {
	repo := &phase2SyncJobRepo{
		job: &caasmmodel.SyncJob{
			Status:       caasmmodel.SyncJobSuccess,
			ErrorMessage: "",
		},
	}
	svc := &CloudAccountService{jobs: repo}

	if svc.persistSyncJobFailureIfActive(context.Background(), &caasmmodel.SyncJob{
		Base:         coremodel.Base{ID: 42},
		ErrorMessage: "collector failed",
	}, time.Now()) {
		t.Fatal("expected failure terminal update to be skipped")
	}
	if repo.updateFieldsCalled {
		t.Fatal("failure update should not run for externally terminal job")
	}
}

func TestDBEventReporterUsesDurableContextForTerminalEvents(t *testing.T) {
	events := &phase2EventRepo{}
	ctx, cancel := context.WithCancel(context.Background())
	cancel()

	reporter := &DBEventReporter{repo: events, syncJobID: 7, ctx: ctx}
	reporter.Report("info", "Job completed successfully", nil)

	if events.createCtxErr != nil {
		t.Fatalf("event create used canceled context: %v", events.createCtxErr)
	}
	if events.created == nil || events.created.SyncJobID != 7 {
		t.Fatalf("event was not recorded correctly: %#v", events.created)
	}
}

func TestZombieReaperDoesNotOverwriteChangedTerminalState(t *testing.T) {
	repo := &phase2SyncJobRepo{
		job: &caasmmodel.SyncJob{Status: caasmmodel.SyncJobFailed},
	}
	reaper := NewZombieReaper(repo)

	reaper.failJob(context.Background(), caasmmodel.SyncJob{
		Base:   coremodel.Base{ID: 42},
		Status: caasmmodel.SyncJobRunning,
	}, "Job timed out")

	if repo.updateFieldsCalled {
		t.Fatal("zombie reaper should not overwrite a terminal job")
	}
}

type phase2SyncJobRepo struct {
	job                *caasmmodel.SyncJob
	updateFieldsCalled bool
	updatedFields      map[string]interface{}
	updateCtxErr       error
}

func (r *phase2SyncJobRepo) Create(context.Context, *caasmmodel.SyncJob) error { return nil }

func (r *phase2SyncJobRepo) Update(context.Context, *caasmmodel.SyncJob) error { return nil }

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

func (r *phase2SyncJobRepo) FindByID(context.Context, uint64) (*caasmmodel.SyncJob, error) {
	return r.job, nil
}

func (r *phase2SyncJobRepo) List(context.Context, repository.SyncJobFilter, int, int) ([]caasmmodel.SyncJob, int64, error) {
	return nil, 0, nil
}

func (r *phase2SyncJobRepo) FindStaleJobs(context.Context, caasmmodel.SyncJobStatus, time.Time) ([]caasmmodel.SyncJob, error) {
	return nil, nil
}

func (r *phase2SyncJobRepo) CountActiveJobs(context.Context) (int64, error) { return 0, nil }

func (r *phase2SyncJobRepo) CountRunningJobs(context.Context) (int64, error) { return 0, nil }

func (r *phase2SyncJobRepo) HasActiveJobForAccount(context.Context, uint64) (bool, error) {
	return false, nil
}

func (r *phase2SyncJobRepo) FindActiveJobForAccount(context.Context, uint64) (*caasmmodel.SyncJob, error) {
	return nil, nil
}

func (r *phase2SyncJobRepo) FindLatestResumableJobForAccount(context.Context, uint64, uint64, string) (*caasmmodel.SyncJob, error) {
	return nil, nil
}

type phase2EventRepo struct {
	created      *caasmmodel.SyncJobEvent
	createCtxErr error
}

func (r *phase2EventRepo) Create(ctx context.Context, event *caasmmodel.SyncJobEvent) error {
	r.createCtxErr = ctx.Err()
	r.created = event
	return nil
}

func (r *phase2EventRepo) List(context.Context, uint64, repository.SyncJobEventListOptions) ([]caasmmodel.SyncJobEvent, error) {
	return nil, nil
}
