// correction.go — the correction gesture as it appears in the chat (V-637).
// Two taps at most: "не то" opens the seven intents, and one of them writes the
// label. The web's version of the same gesture is cmd/mavweb/chat.go.
package telegramsink

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

// CorrectionTargets — the intents a correction may name, in the order the
// buttons are drawn. It mirrors the seven the web offers, and it is a closed
// list for the same reason: V-632 fits prototypes from the label table, and a
// label nothing can score is worse than no label.
var CorrectionTargets = []string{"fact", "note", "reminder", "query", "act", "chat", "system"}

// correctionKeyboard — the one gesture beside the reply. Nothing when the turn
// did not persist: a button that cannot name a row would report a failure the
// owner cannot act on.
func (p *Poller) correctionKeyboard(traceID int64) *inlineKeyboard {
	if traceID <= 0 || p.correct == nil {
		return nil
	}
	return &inlineKeyboard{Rows: [][]inlineButton{{
		{Text: "не то", Data: fmt.Sprintf("%s%d", prefixAsk, traceID)},
	}}}
}

// targetKeyboard — the seven intents, plus the cheap half kept reachable. He
// opened the row without knowing he had to name something, and closing it with
// no way out would price the negative he was willing to give.
func targetKeyboard(traceID int64) *inlineKeyboard {
	var rows [][]inlineButton
	row := []inlineButton{}
	for _, t := range CorrectionTargets {
		row = append(row, inlineButton{Text: t, Data: fmt.Sprintf("%s%d:%s", prefixTarget, traceID, t)})
		if len(row) == 4 {
			rows, row = append(rows, row), nil
		}
	}
	if len(row) > 0 {
		rows = append(rows, row)
	}
	return &inlineKeyboard{Rows: append(rows, []inlineButton{
		{Text: "просто неверно", Data: fmt.Sprintf("%s%d:", prefixTarget, traceID)},
	})}
}

// Callback data is capped at 64 bytes by telegram, so it carries the trace id
// and the target and nothing else.
const (
	prefixAsk    = "w:"
	prefixTarget = "t:"
)

type callbackKind int

const (
	callbackUnknown callbackKind = iota
	callbackAskTarget
	callbackTarget
)

// parseCallback reads button data. An unparseable id, or a target that is not
// one of the seven, is callbackUnknown — the data came off the wire, and a
// label the fitting code cannot score is worse than no label.
func parseCallback(data string) (traceID int64, target string, kind callbackKind) {
	switch {
	case strings.HasPrefix(data, prefixAsk):
		id, err := strconv.ParseInt(strings.TrimPrefix(data, prefixAsk), 10, 64)
		if err != nil || id <= 0 {
			return 0, "", callbackUnknown
		}
		return id, "", callbackAskTarget
	case strings.HasPrefix(data, prefixTarget):
		rest := strings.TrimPrefix(data, prefixTarget)
		idPart, target, ok := strings.Cut(rest, ":")
		if !ok {
			return 0, "", callbackUnknown
		}
		id, err := strconv.ParseInt(idPart, 10, 64)
		if err != nil || id <= 0 {
			return 0, "", callbackUnknown
		}
		if target != "" && !isCorrectionTarget(target) {
			return 0, "", callbackUnknown
		}
		return id, target, callbackTarget
	}
	return 0, "", callbackUnknown
}

func isCorrectionTarget(s string) bool {
	for _, t := range CorrectionTargets {
		if t == s {
			return true
		}
	}
	return false
}
