package validate

import (
	"strings"

	contracterrors "bit.admedia.com/scm/ad/adpilot-indexing-code-parser.com/internal/contracts/errors"
	"bit.admedia.com/scm/ad/adpilot-indexing-code-parser.com/internal/contracts/events"
	"bit.admedia.com/scm/ad/adpilot-indexing-code-parser.com/internal/contracts/metadata"
)

func envelope(env metadata.Envelope) error {
	if strings.TrimSpace(env.EventID) == "" {
		return contracterrors.New(contracterrors.CodeMissingField, "event_id", "event_id is required")
	}
	if strings.TrimSpace(env.EventVersion) == "" {
		return contracterrors.New(contracterrors.CodeMissingField, "event_version", "event_version is required")
	}
	if env.EventVersion != metadata.EventVersionV1 {
		return contracterrors.New(contracterrors.CodeUnsupportedVersion, "event_version", "unsupported event version")
	}
	if env.CreatedAt.IsZero() {
		return contracterrors.New(contracterrors.CodeMissingField, "created_at", "created_at is required")
	}
	return nil
}

func repoCommit(repoID, commitSHA string) error {
	if strings.TrimSpace(repoID) == "" {
		return contracterrors.New(contracterrors.CodeMissingField, "repo_id", "repo_id is required")
	}
	if strings.TrimSpace(commitSHA) == "" {
		return contracterrors.New(contracterrors.CodeMissingField, "commit_sha", "commit_sha is required")
	}
	return nil
}

func repoSnapshot(repoID, snapshotID, commitSHA string) error {
	if err := repoCommit(repoID, commitSHA); err != nil {
		return err
	}
	if strings.TrimSpace(snapshotID) == "" {
		return contracterrors.New(contracterrors.CodeMissingField, "snapshot_id", "snapshot_id is required")
	}
	return nil
}

// FileChanged validates a files.changed event.
func FileChanged(event events.FileChangedEvent) error {
	if err := envelope(event.Envelope); err != nil {
		return err
	}
	if err := repoCommit(event.RepoID, event.CommitSHA); err != nil {
		return err
	}
	if strings.TrimSpace(event.SnapshotID) == "" {
		return contracterrors.New(contracterrors.CodeMissingField, "snapshot_id", "snapshot_id is required")
	}
	if strings.TrimSpace(event.FilePath) == "" {
		return contracterrors.New(contracterrors.CodeMissingField, "file_path", "file_path is required")
	}
	if strings.TrimSpace(event.Language) == "" {
		return contracterrors.New(contracterrors.CodeMissingField, "language", "language is required")
	}
	if strings.TrimSpace(string(event.FileKind)) == "" {
		return contracterrors.New(contracterrors.CodeMissingField, "file_kind", "file_kind is required")
	}
	if !event.FileKind.IsValid() {
		return contracterrors.New(contracterrors.CodeInvalidField, "file_kind", "unsupported file_kind")
	}
	if strings.TrimSpace(string(event.ChangeType)) == "" {
		return contracterrors.New(contracterrors.CodeMissingField, "change_type", "change_type is required")
	}
	if !event.ChangeType.IsValid() {
		return contracterrors.New(contracterrors.CodeInvalidField, "change_type", "unsupported change_type")
	}
	return nil
}

// RepoSnapshotReady validates a repo.snapshot.ready event.
func RepoSnapshotReady(event events.RepoSnapshotReadyEvent) error {
	if err := envelope(event.Envelope); err != nil {
		return err
	}
	if err := repoCommit(event.RepoID, event.CommitSHA); err != nil {
		return err
	}
	if strings.TrimSpace(event.SnapshotID) == "" {
		return contracterrors.New(contracterrors.CodeMissingField, "snapshot_id", "snapshot_id is required")
	}
	if strings.TrimSpace(event.Ref) == "" {
		return contracterrors.New(contracterrors.CodeMissingField, "ref", "ref is required")
	}
	return nil
}

// CommitsChanged validates a commits.changed event.
func CommitsChanged(event events.CommitsChangedEvent) error {
	if err := envelope(event.Envelope); err != nil {
		return err
	}
	if err := repoCommit(event.RepoID, event.CommitSHA); err != nil {
		return err
	}
	if strings.TrimSpace(event.SnapshotID) == "" {
		return contracterrors.New(contracterrors.CodeMissingField, "snapshot_id", "snapshot_id is required")
	}
	return nil
}

// ChunksReady validates a chunks.ready event.
func ChunksReady(event events.ChunksReadyEvent) error {
	if err := envelope(event.Envelope); err != nil {
		return err
	}
	if err := repoSnapshot(event.RepoID, event.SnapshotID, event.CommitSHA); err != nil {
		return err
	}
	if strings.TrimSpace(event.FilePath) == "" {
		return contracterrors.New(contracterrors.CodeMissingField, "file_path", "file_path is required")
	}
	if strings.TrimSpace(event.Language) == "" {
		return contracterrors.New(contracterrors.CodeMissingField, "language", "language is required")
	}
	if len(event.Chunks) == 0 {
		return contracterrors.New(contracterrors.CodeMissingField, "chunks", "chunks is required")
	}
	return nil
}

// GraphArtifactReady validates a graph.artifact.ready event.
func GraphArtifactReady(event events.GraphArtifactReadyEvent) error {
	if err := envelope(event.Envelope); err != nil {
		return err
	}
	if err := repoCommit(event.RepoID, event.CommitSHA); err != nil {
		return err
	}
	if strings.TrimSpace(event.ArtifactID) == "" {
		return contracterrors.New(contracterrors.CodeMissingField, "artifact_id", "artifact_id is required")
	}
	if strings.TrimSpace(event.ArtifactURI) == "" {
		return contracterrors.New(contracterrors.CodeMissingField, "artifact_uri", "artifact_uri is required")
	}
	if strings.TrimSpace(event.ParserVersion) == "" {
		return contracterrors.New(contracterrors.CodeMissingField, "parser_version", "parser_version is required")
	}
	if strings.TrimSpace(event.GraphSchemaVersion) == "" {
		return contracterrors.New(contracterrors.CodeMissingField, "graph_schema_version", "graph_schema_version is required")
	}
	return nil
}

// GraphDeltaReady validates a graph.delta.ready event.
func GraphDeltaReady(event events.GraphDeltaReadyEvent) error {
	if err := envelope(event.Envelope); err != nil {
		return err
	}
	if strings.TrimSpace(event.RepoID) == "" {
		return contracterrors.New(contracterrors.CodeMissingField, "repo_id", "repo_id is required")
	}
	if strings.TrimSpace(event.BaseCommitSHA) == "" {
		return contracterrors.New(contracterrors.CodeMissingField, "base_commit_sha", "base_commit_sha is required")
	}
	if strings.TrimSpace(event.TargetCommitSHA) == "" {
		return contracterrors.New(contracterrors.CodeMissingField, "target_commit_sha", "target_commit_sha is required")
	}
	if strings.TrimSpace(event.DeltaArtifactID) == "" {
		return contracterrors.New(contracterrors.CodeMissingField, "delta_artifact_id", "delta_artifact_id is required")
	}
	if strings.TrimSpace(event.DeltaArtifactURI) == "" {
		return contracterrors.New(contracterrors.CodeMissingField, "delta_artifact_uri", "delta_artifact_uri is required")
	}
	if strings.TrimSpace(event.GraphSchemaVersion) == "" {
		return contracterrors.New(contracterrors.CodeMissingField, "graph_schema_version", "graph_schema_version is required")
	}
	return nil
}

// DocsParseCompleted validates a docs.parse.completed event.
func DocsParseCompleted(event events.DocsParseCompletedEvent) error {
	if err := envelope(event.Envelope); err != nil {
		return err
	}
	if strings.TrimSpace(event.RepoID) == "" {
		return contracterrors.New(contracterrors.CodeMissingField, "repo_id", "repo_id is required")
	}
	if strings.TrimSpace(event.SnapshotID) == "" {
		return contracterrors.New(contracterrors.CodeMissingField, "snapshot_id", "snapshot_id is required")
	}
	return nil
}

// ParserDiagnosticsReady validates a parser.diagnostics.ready event.
func ParserDiagnosticsReady(event events.ParserDiagnosticsReadyEvent) error {
	if err := envelope(event.Envelope); err != nil {
		return err
	}
	if err := repoSnapshot(event.RepoID, event.SnapshotID, event.CommitSHA); err != nil {
		return err
	}
	if strings.TrimSpace(event.JobID) == "" {
		return contracterrors.New(contracterrors.CodeMissingField, "job_id", "job_id is required")
	}
	return nil
}
