package server

import (
	"context"
	"encoding/json"
	"errors"
	"strconv"
	"strings"
	"time"

	agsserver "github.com/pingcap/agent-git-service/server"

	"github.com/tidbcloud/AgentTeam/internal/core/identifiers"
	"github.com/tidbcloud/AgentTeam/internal/store/kb"
	"github.com/tidbcloud/AgentTeam/internal/store/resource/repos"
)

// agsKBSourceRevisionEventBridge owns current-revision advancement for
// user-managed expected source repositories. Mirror jobs publish revisions
// synchronously at their own authoritative completion point instead.
type agsKBSourceRevisionEventBridge struct {
	repositories     *repos.Store
	kb               *kb.Store
	contentAuthority kbSourceEventAuthorityReader
}

type agsKBWebhookPayload struct {
	Ref        string `json:"ref"`
	Before     string `json:"before"`
	After      string `json:"after"`
	Deleted    bool   `json:"deleted"`
	Repository struct {
		FullName string `json:"full_name"`
	} `json:"repository"`
}

func (b *agsKBSourceRevisionEventBridge) HandleRepositoryEvent(ctx context.Context, event agsserver.RepositoryEvent) error {
	if b == nil || b.repositories == nil || b.kb == nil || strings.TrimSpace(event.Event) != "push" {
		return nil
	}
	var payload agsKBWebhookPayload
	if len(event.PayloadJSON) == 0 || json.Unmarshal(event.PayloadJSON, &payload) != nil {
		return nil
	}
	repos, err := b.repositories.ListByAGSRepoID(ctx, strconv.FormatUint(uint64(event.RepositoryID), 10))
	if err != nil {
		return err
	}
	if len(repos) == 0 && strings.TrimSpace(payload.Repository.FullName) != "" {
		repos, err = b.repositories.ListByRepoFullName(ctx, payload.Repository.FullName)
		if err != nil {
			return err
		}
	}
	nativeKBSources, err := nativeKBSourceEventBindings(ctx, b.contentAuthority, repos)
	if err != nil {
		return err
	}
	var joined error
	for _, repo := range repos {
		if _, native := nativeKBSources[kbSourceEventBinding{workspaceID: strings.TrimSpace(repo.WorkspaceID), sourceID: strings.TrimSpace(repo.ResourceID)}]; native {
			continue
		}
		if err := b.advanceLatestRevision(ctx, repo, payload); err != nil {
			joined = errors.Join(joined, err)
		}
	}
	return joined
}

func (b *agsKBSourceRevisionEventBridge) advanceLatestRevision(ctx context.Context, repo repos.ResourceRepository, payload agsKBWebhookPayload) error {
	if strings.TrimSpace(repo.ResourceType) != repos.ResourceTypeKBSource || strings.TrimSpace(repo.RepoRole) != repos.RepoRolePrimary {
		return nil
	}
	defaultRef := strings.TrimSpace(repo.DefaultRef)
	if defaultRef == "" {
		defaultRef = "main"
	}
	ref := strings.TrimSpace(payload.Ref)
	if payload.Deleted || !strings.HasPrefix(ref, "refs/heads/") || strings.TrimPrefix(ref, "refs/heads/") != defaultRef {
		return nil
	}
	after := strings.TrimSpace(payload.After)
	if !isGitCommitSHA(after) || after == "0000000000000000000000000000000000000000" {
		return nil
	}
	before := strings.TrimSpace(payload.Before)
	if before == "0000000000000000000000000000000000000000" {
		before = ""
	} else if !isGitCommitSHA(before) {
		// A push without an immutable predecessor cannot safely advance the
		// source authority because delayed delivery could roll it backwards.
		return nil
	}
	source, err := b.kb.GetSourceByID(ctx, repo.WorkspaceID, repo.ResourceID)
	if err != nil {
		return err
	}
	revisionID, err := identifiers.NewEntityID("rev")
	if err != nil {
		return err
	}
	now := time.Now().UTC()
	_, _, err = b.kb.AdvanceSourceRevision(ctx, kb.AdvanceSourceRevisionParams{
		ExpectedCurrentSourceCommitSHA: before,
		EnforceExpectedCurrent:         true,
		Revision: kb.SourceRevision{
			ID: revisionID, WorkspaceID: source.WorkspaceID, KnowledgeBaseID: source.KnowledgeBaseID,
			SourceID: source.ID, SourceCommitSHA: after, FreshnessStatus: "current",
			CreatedAt: now, UpdatedAt: now,
		}})
	return err
}

func isGitCommitSHA(value string) bool {
	if len(value) != 40 {
		return false
	}
	for _, ch := range value {
		if (ch < '0' || ch > '9') && (ch < 'a' || ch > 'f') && (ch < 'A' || ch > 'F') {
			return false
		}
	}
	return true
}
