package dbsync

import (
	"context"
	"fmt"
	"strings"
	"time"

	"github.com/google/uuid"
)

// RollbackLwwOverride 超管按落败快照回滚线上 A 单行。
// 仅 outcome=applied_source 可回滚：把 loser_payload 写回 remote；无快照则按 pk 删除。
func RollbackLwwOverride(ctx context.Context, store *FileStore, overrideID string) (*LwwOverride, error) {
	if store == nil {
		return nil, fmt.Errorf("store is nil")
	}
	o, err := store.GetLwwOverride(overrideID)
	if err != nil {
		return nil, err
	}
	if o.Outcome != OutcomeApplied {
		return nil, fmt.Errorf("仅「源端覆盖」记录可回滚（当前 outcome=%s）", o.Outcome)
	}
	if strings.TrimSpace(o.ChannelID) == "" {
		return nil, fmt.Errorf("override missing channel_id")
	}
	ch, err := store.GetChannel(o.ChannelID)
	if err != nil {
		return nil, fmt.Errorf("channel: %w", err)
	}
	table := strings.TrimSpace(o.Table)
	pkCol := "id"
	if ch.PKColumns != nil && strings.TrimSpace(ch.PKColumns[table]) != "" {
		pkCol = ch.PKColumns[table]
	}
	db, err := Open(ch.Remote.Driver, ch.Remote.DSN)
	if err != nil {
		return nil, fmt.Errorf("open remote: %w", err)
	}
	defer db.Close()
	if err := EnsureMeta(ctx, db, ch.Remote.Driver); err != nil {
		return nil, err
	}

	before := SnapshotTargetRow(ctx, db, ch.Remote.Driver, table, pkCol, o.RowPK)
	ver := time.Now().UnixNano()
	loser := strings.TrimSpace(o.LoserPayload)
	op := "upsert"
	payload := loser
	if loser == "" || loser == "{}" {
		op = "delete"
		payload = fmt.Sprintf(`{%q:%q}`, pkCol, o.RowPK)
	}
	if err := ApplyChange(ctx, db, ch.Remote.Driver, table, pkCol, op, payload, ver); err != nil {
		return nil, fmt.Errorf("apply rollback: %w", err)
	}

	rec := LwwOverride{
		ID:            uuid.NewString(),
		TenantID:      o.TenantID,
		ChannelID:     o.ChannelID,
		Table:         o.Table,
		RowPK:         o.RowPK,
		Op:            op,
		Entry:         EntryRollback,
		Policy:        "manual_rollback",
		Outcome:       OutcomeRolled,
		LoserPayload:  before,
		WinnerPayload: payload,
		TargetVer:     o.SourceVer,
		SourceVer:     ver,
		CreatedAt:     time.Now().UTC(),
	}
	if err := store.AddLwwOverride(rec); err != nil {
		return nil, err
	}
	return &rec, nil
}
