package canonical

import (
	"fmt"
	"strconv"
	"strings"
	"time"
)

const markerPrefix = "<!-- ags-sync:link=v1:"

type ManagedBlock struct {
	Body          string
	ObjectLinkID  uint
	SourceURL     string
	OriginalActor string
	OriginalTime  *time.Time
}

func RenderManagedBody(body, sourceURL, actor string, originalTime *time.Time, objectLinkID uint) string {
	return renderManagedBody(body, sourceURL, actor, originalTime, objectLinkID, "")
}

// RenderManagedBodyWithNotice adds target-representation context inside the
// managed block. StripManagedBody removes the notice together with the marker,
// so it remains visible to humans without contaminating the canonical body.
func RenderManagedBodyWithNotice(body, sourceURL, actor string, originalTime *time.Time, objectLinkID uint, notice string) string {
	return renderManagedBody(body, sourceURL, actor, originalTime, objectLinkID, notice)
}

func renderManagedBody(body, sourceURL, actor string, originalTime *time.Time, objectLinkID uint, notice string) string {
	body = strings.TrimRight(body, "\n")
	parts := make([]string, 0, 4)
	if strings.TrimSpace(notice) != "" {
		parts = append(parts, strings.TrimSpace(notice))
	}
	if strings.TrimSpace(sourceURL) != "" {
		parts = append(parts, "Synced from [source]("+sourceURL+")")
	}
	if strings.TrimSpace(actor) != "" {
		parts = append(parts, "Original author: "+actor)
	}
	if originalTime != nil && !originalTime.IsZero() {
		parts = append(parts, originalTime.UTC().Format(time.RFC3339))
	}
	visible := strings.Join(parts, " · ")
	if visible == "" {
		visible = "Synced by AGS repository sync"
	}
	return fmt.Sprintf("%s\n\n---\n> %s\n%s%d -->", body, visible, markerPrefix, objectLinkID)
}

// StripManagedBody only recognizes a complete terminal block. Callers still
// validate the returned ObjectLink ID and target identity before treating the
// marker as ownership; an unlinked object remains unmanaged regardless of the
// marker text.
func StripManagedBody(body string) ManagedBlock {
	trimmed := strings.TrimRight(body, "\n")
	markerAt := strings.LastIndex(trimmed, markerPrefix)
	if markerAt < 0 || !strings.HasSuffix(trimmed, " -->") {
		return ManagedBlock{Body: body}
	}
	idText := strings.TrimSuffix(trimmed[markerAt+len(markerPrefix):], " -->")
	id, err := strconv.ParseUint(strings.TrimSpace(idText), 10, 64)
	if err != nil || id == 0 {
		return ManagedBlock{Body: body}
	}
	separatorAt := strings.LastIndex(trimmed[:markerAt], "\n\n---\n>")
	if separatorAt < 0 {
		return ManagedBlock{Body: body}
	}
	return ManagedBlock{Body: strings.TrimRight(trimmed[:separatorAt], "\n"), ObjectLinkID: uint(id)}
}
