package commitintel

import (
	"context"
	"fmt"
	"log/slog"
	"strings"
	"time"

	"bit.admedia.com/scm/ad/adpilot-indexing-commit-intel.com/internal/contracts/events"
	"bit.admedia.com/scm/ad/adpilot-indexing-commit-intel.com/internal/contracts/graph"
	"bit.admedia.com/scm/ad/adpilot-indexing-commit-intel.com/internal/contracts/metadata"
	"bit.admedia.com/scm/ad/adpilot-indexing-commit-intel.com/internal/contracts/validate"
	"bit.admedia.com/scm/ad/adpilot-indexing-commit-intel.com/internal/store"
)

// ProcessGraphDeltaReady loads a graph delta, runs commit analysis, persists, and publishes.
func (s *Service) ProcessGraphDeltaReady(ctx context.Context, event events.GraphDeltaReadyEvent) error {
	if err := validate.GraphDeltaReady(event); err != nil {
		return fmt.Errorf("validate graph.delta.ready: %w", err)
	}

	if s.analysisWriter != nil {
		exists, err := s.analysisWriter.ExistsByEventID(ctx, event.EventID)
		if err != nil {
			return fmt.Errorf("check analysis idempotency: %w", err)
		}
		if exists {
			slog.Info("analysis job skipped idempotent",
				"event_id", event.EventID,
				"repo_id", event.RepoID,
			)
			return nil
		}
	} else if s.jobs != nil && s.jobs.WasProcessed(event.EventID) {
		slog.Info("analysis job skipped idempotent",
			"event_id", event.EventID,
			"repo_id", event.RepoID,
		)
		return nil
	}

	if s.reader == nil {
		return fmt.Errorf("artifact reader is not configured")
	}
	if s.analyzer == nil {
		return fmt.Errorf("impact analyzer is not configured")
	}

	jobID := JobIDFromEvent(event)
	analysisID := AnalysisIDFromEvent(event)
	started := time.Now().UTC()
	processStarted := time.Now()

	if s.jobs != nil {
		s.jobs.SaveJob(store.AnalysisJob{
			JobID:           jobID,
			EventID:         event.EventID,
			RepoID:          event.RepoID,
			BaseCommitSHA:   event.BaseCommitSHA,
			TargetCommitSHA: event.TargetCommitSHA,
			Status:          store.JobStatusRunning,
			StartedAt:       started,
		})
	}

	delta, err := s.reader.LoadDeltaByURI(ctx, event.DeltaArtifactURI)
	if err != nil {
		s.markJobFailed(jobID, err.Error())
		return fmt.Errorf("load delta: %w", err)
	}

	analysis, err := s.analyzer.AnalyzeDelta(delta, event, analysisID)
	if err != nil {
		s.markJobFailed(jobID, err.Error())
		return fmt.Errorf("analyze delta: %w", err)
	}

	gitMeta := resolveGitMetadata(event, delta)

	result := store.AnalysisResult{
		AnalysisID:        analysis.AnalysisID,
		RepoID:            analysis.RepoID,
		SnapshotID:        delta.SnapshotID,
		CommitSHA:         analysis.CommitSHA,
		BaseCommitSHA:     event.BaseCommitSHA,
		ImpactedSymbols:   append([]string(nil), analysis.ImpactedSymbols...),
		ChangedFiles:      append([]string(nil), analysis.ChangedFiles...),
		Summary:           analysis.Summary,
		RelatedDocChunks:  []string{},
		GitCommitMetadata: gitMeta,
	}

	if s.analysisWriter != nil {
		doc := store.CommitAnalysisDoc{
			AnalysisID:        result.AnalysisID,
			RepoID:            result.RepoID,
			SnapshotID:        result.SnapshotID,
			CommitSHA:         result.CommitSHA,
			BaseCommitSHA:     result.BaseCommitSHA,
			ChangedFiles:      result.ChangedFiles,
			ImpactedSymbols:   result.ImpactedSymbols,
			Summary:           result.Summary,
			RelatedDocChunks:  result.RelatedDocChunks,
			EventID:           event.EventID,
			DeltaID:           event.DeltaArtifactID,
			GitCommitMetadata: gitMeta,
		}
		if err := s.analysisWriter.SaveAnalysis(ctx, doc); err != nil {
			s.markJobFailed(jobID, err.Error())
			return fmt.Errorf("save commit analysis: %w", err)
		}
	}

	if s.events != nil {
		outEvent := CommitAnalysisReadyFromResult(event, result)
		if err := s.events.PublishCommitAnalysisReady(ctx, outEvent); err != nil {
			s.markJobFailed(jobID, err.Error())
			return fmt.Errorf("publish commit.analysis.ready: %w", err)
		}
	}

	if s.jobs != nil {
		s.jobs.MarkCompleted(jobID, result)
	}

	if s.indexingRuns != nil && strings.TrimSpace(result.SnapshotID) != "" {
		if err := s.indexingRuns.UpdateCommitIntelStage(ctx, result.RepoID, result.SnapshotID); err != nil {
			return fmt.Errorf("update indexing run commit_intel stage: %w", err)
		}
	}

	slog.Info("analysis job completed",
		"job_id", jobID,
		"analysis_id", analysisID,
		"event_id", event.EventID,
		"repo_id", event.RepoID,
		"target_commit_sha", event.TargetCommitSHA,
		"duration_ms", time.Since(processStarted).Milliseconds(),
	)

	return nil
}

func resolveGitMetadata(event events.GraphDeltaReadyEvent, delta graph.Delta) metadata.GitCommitMetadata {
	if event.GitCommitMetadata.HasMetadata() {
		return event.GitCommitMetadata
	}
	return delta.GitCommit
}

// RecordGraphDeltaFailure records a permanent graph-delta processing failure.
func (s *Service) RecordGraphDeltaFailure(ctx context.Context, event events.GraphDeltaReadyEvent, message string) error {
	if s.indexingRuns == nil || s.reader == nil {
		return nil
	}

	delta, err := s.reader.LoadDeltaByURI(ctx, event.DeltaArtifactURI)
	if err != nil {
		return fmt.Errorf("load delta for failure accounting: %w", err)
	}
	if strings.TrimSpace(delta.SnapshotID) == "" {
		return nil
	}
	return s.indexingRuns.IncrementFailedCommit(
		ctx,
		event.RepoID,
		delta.SnapshotID,
		event.TargetCommitSHA,
		event.EventID,
		message,
	)
}
