package mynalog

import (
	"fmt"
	"net/url"
	"strings"
	"unicode/utf8"

	"github.com/google/uuid"
)

// maxNameRunes is the tax service's ceiling on a service name, counted in characters rather than
// bytes — the names here are Cyrillic, so the two differ by a factor of two.
const maxNameRunes = 128

// markerRunes is how much of the order id rides in the name.
//
// It is taken from the END of the identifier, and that is not cosmetic. Our order ids are UUIDv7,
// whose leading hex digits are a millisecond timestamp: the first eight of them are the high 32
// bits of that timestamp, so they only change about once a minute and every order placed within the
// same minute would share them. The trailing digits are random. Since this marker is the sole key
// the recovery probe matches on, a shared marker would make two purchases indistinguishable — which
// is precisely the situation that ends in an income filed twice or not at all.
const markerRunes = 8

// defaultTitle describes the goods when the product carries no usable title of its own. It has to
// read as a plain description of what was sold, because the tax service and the buyer both see it.
const defaultTitle = "Цифровые ценности"

// IncomeName builds the service name for one purchase: a human-readable description of what was
// sold, followed by a marker derived from the order id.
//
// The marker is not decoration. Registering an income is not idempotent, so after a timeout the
// only way to discover whether the receipt exists is to search the tax service's income list by
// this exact string — and without the marker two identical chip packs would be indistinguishable,
// which would either duplicate the income or lose it. The result is capped at maxNameRunes with the
// description giving way first: the marker is never truncated.
//
// chips is the pack size; when it is zero the product is not a chip pack and fallbackTitle
// describes it instead.
func IncomeName(chips int, orderID uuid.UUID, fallbackTitle string) string {
	desc := strings.TrimSpace(fallbackTitle)
	if chips > 0 {
		desc = fmt.Sprintf("Внутриигровая валюта: %q, %d шт.", "Фишка", chips)
	}
	if desc == "" {
		desc = defaultTitle
	}
	suffix := fmt.Sprintf(" (ID: %s)", orderMarker(orderID))
	budget := maxNameRunes - utf8.RuneCountInString(suffix)
	if budget < 1 {
		return suffix
	}
	if utf8.RuneCountInString(desc) > budget {
		desc = strings.TrimSpace(string([]rune(desc)[:budget-1])) + "…"
	}
	return desc + suffix
}

// orderMarker renders the part of the order id that rides in a service name: its last markerRunes
// hex digits, which for a UUIDv7 are random rather than clock-derived.
func orderMarker(orderID uuid.UUID) string {
	s := orderID.String()
	if len(s) < markerRunes {
		return s
	}
	return s[len(s)-markerRunes:]
}

// ReceiptURL is where the printable receipt for a registered income lives. It is what the buyer is
// sent as their proof of purchase, so it is a package function rather than a method: the letter is
// composed far from any authenticated client.
func ReceiptURL(baseURL, inn, receiptUUID string) string {
	if baseURL == "" {
		baseURL = DefaultBaseURL
	}
	return fmt.Sprintf("%s/api/v1/receipt/%s/%s/print",
		strings.TrimSuffix(baseURL, "/"), url.PathEscape(inn), url.PathEscape(receiptUUID))
}

// ReceiptURL is where the printable receipt for a registered income lives, against this client's
// service root.
func (c *Client) ReceiptURL(inn, receiptUUID string) string {
	return ReceiptURL(c.baseURL, inn, receiptUUID)
}
