package service

import (
	"context"
	"crypto/sha256"
	"encoding/hex"
	"fmt"

	gitcontract "bit.admedia.com/scm/ad/adpilot-indexing-repo-sync.com/internal/contracts/git"
	"bit.admedia.com/scm/ad/adpilot-indexing-repo-sync.com/internal/contracts/events"
)

func (s *SyncService) publishSyncResult(ctx context.Context, result gitcontract.SyncResult) error {
	snap := result.Snapshot

	snapshotEvent := events.NewRepoSnapshotReadyEvent(
		snapshotEventID(snap.SnapshotID),
		snap.RepoID,
		snap.SnapshotID,
		snap.CommitSHA,
		snap.Ref,
	)
	snapshotEvent.FileCount = len(result.FileChanges)

	if err := s.events.PublishRepoSnapshotReady(ctx, snapshotEvent); err != nil {
		return fmt.Errorf("publish repo.snapshot.ready: %w", err)
	}
	s.logger.Info("published snapshot ready event",
		"repo_id", snap.RepoID,
		"snapshot_id", snap.SnapshotID,
		"event_id", snapshotEvent.EventID,
	)

	for i, change := range result.FileChanges {
		event := events.NewFileChangedEvent(
			fileEventID(snap.SnapshotID, change),
			snap.RepoID,
			snap.SnapshotID,
			snap.CommitSHA,
			change.Path,
			change.Language,
			change.FileKind,
			change.ChangeType,
		)
		if change.OldPath != "" {
			event.OldPath = change.OldPath
		}
		if err := s.events.PublishFileChanged(ctx, event); err != nil {
			s.logger.Error("partial publish failure",
				"repo_id", snap.RepoID,
				"snapshot_id", snap.SnapshotID,
				"published_files", i,
				"total_files", len(result.FileChanges),
				"error", err,
			)
			return fmt.Errorf("publish files.changed for %q: %w", change.Path, err)
		}
	}

	for i, commit := range result.CommitChanges {
		event := events.NewCommitsChangedEvent(
			commitEventID(snap.SnapshotID, commit.CommitSHA),
			snap.RepoID,
			snap.SnapshotID,
			commit.CommitSHA,
		)
		event.Ref = snap.Ref
		if commit.ParentSHA != "" {
			event.ParentSHA = commit.ParentSHA
		}
		event.GitCommitMetadata = commit.GitCommitMetadata
		if author := commit.FormatAuthor(); author != "" {
			event.Author = author
		}
		if commit.CommitMessage != "" {
			event.Message = commit.CommitMessage
		}
		if err := s.events.PublishCommitsChanged(ctx, event); err != nil {
			s.logger.Error("partial publish failure",
				"repo_id", snap.RepoID,
				"snapshot_id", snap.SnapshotID,
				"published_commits", i,
				"total_commits", len(result.CommitChanges),
				"error", err,
			)
			return fmt.Errorf("publish commits.changed for %q: %w", commit.CommitSHA, err)
		}
	}
	s.logger.Info("published commit events",
		"repo_id", snap.RepoID,
		"snapshot_id", snap.SnapshotID,
		"commit_count", len(result.CommitChanges),
	)
	return nil
}

func snapshotEventID(snapshotID string) string {
	return fmt.Sprintf("evt_%s_ready", snapshotID)
}

func fileEventID(snapshotID string, change gitcontract.FileChange) string {
	key := change.Path
	if change.OldPath != "" {
		key = change.OldPath + "->" + change.Path
	}
	sum := sha256.Sum256([]byte(key))
	return fmt.Sprintf("evt_%s_file_%s", snapshotID, hex.EncodeToString(sum[:4]))
}

func commitEventID(snapshotID, commitSHA string) string {
	sum := sha256.Sum256([]byte(commitSHA))
	return fmt.Sprintf("evt_%s_commit_%s", snapshotID, hex.EncodeToString(sum[:4]))
}
