// Package autonumber implements metadata-driven identifier assignment for NexusCRM.
package autonumber

import (
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

// TestRender_BasicDateCounter covers PRD §10.1 case 1 (simple counter
// + literal) and case 2 combined with the PRD §6.3 sample row for
// `IBG-B-{YYYY}{MM}{DD}-{000000}`.
//
// Wave 1: Render returns ("", errNotImplemented); the assertions here
// are the RED state.
func TestRender_BasicDateCounter(t *testing.T) {
	t.Parallel()

	assignedAt := time.Date(2026, 4, 17, 12, 0, 0, 0, time.UTC)

	// Per PRD §10.1 case 1: basic `{0000}` with counter 1..999.
	t.Run("case 1 — basic {0000} counter=1 → 0001", func(t *testing.T) {
		t.Parallel()
		spec := FormatSpec{
			Raw:          "{0000}",
			CounterWidth: 4,
			Tokens: []Token{
				{Kind: TokenKindCounter, Width: 4},
			},
		}
		got, err := Render(spec, assignedAt, 1, nil)
		require.NoError(t, err, "Render of valid spec must succeed")
		assert.Equal(t, "0001", got)
	})

	t.Run("case 1 — basic {0000} counter=999 → 0999", func(t *testing.T) {
		t.Parallel()
		spec := FormatSpec{
			Raw:          "{0000}",
			CounterWidth: 4,
			Tokens: []Token{
				{Kind: TokenKindCounter, Width: 4},
			},
		}
		got, err := Render(spec, assignedAt, 999, nil)
		require.NoError(t, err)
		assert.Equal(t, "0999", got)
	})

	// Per PRD §10.1 case 1 tail: counter at the boundary before
	// overflow (10^width - 1) renders without error; overflow itself
	// is tested in the engine layer (§5.1 step 5).
	t.Run("case 1 — {0000} counter=9999 → 9999", func(t *testing.T) {
		t.Parallel()
		spec := FormatSpec{
			Raw:          "{0000}",
			CounterWidth: 4,
			Tokens: []Token{
				{Kind: TokenKindCounter, Width: 4},
			},
		}
		got, err := Render(spec, assignedAt, 9999, nil)
		require.NoError(t, err)
		assert.Equal(t, "9999", got)
	})

	// Per PRD §10.1 case 2: `IBG-B-{YYYY}{MM}{DD}-{000000}` + counter
	// 3968 + assignedAt=2026-04-18 → `IBG-B-20260418-003968`.
	t.Run("case 2 — IBG-B-{YYYY}{MM}{DD}-{000000}", func(t *testing.T) {
		t.Parallel()
		spec := FormatSpec{
			Raw:          "IBG-B-{YYYY}{MM}{DD}-{000000}",
			CounterWidth: 6,
			Tokens: []Token{
				{Kind: TokenKindLiteral, Lit: "IBG-B-"},
				{Kind: TokenKindDate, DateName: "YYYY"},
				{Kind: TokenKindDate, DateName: "MM"},
				{Kind: TokenKindDate, DateName: "DD"},
				{Kind: TokenKindLiteral, Lit: "-"},
				{Kind: TokenKindCounter, Width: 6},
			},
		}
		assignedAtCase2 := time.Date(2026, 4, 18, 10, 30, 0, 0, time.UTC)
		got, err := Render(spec, assignedAtCase2, 3968, nil)
		require.NoError(t, err)
		assert.Equal(t, "IBG-B-20260418-003968", got)
	})
}

// TestRender_RecordTypeCode covers PRD §10.1 case 2 extended by the
// §6.3 RecordType example:
//
//	{RecordType.RecordTypeCode}-{YY}{MM}{DD}-{00}
//	→ CSA-260417-01
//
// The renderer reads `crossValues[tok.RefKey]`; the RefKey convention
// for the RecordType token is the literal `{RecordType.RecordTypeCode}`.
func TestRender_RecordTypeCode(t *testing.T) {
	t.Parallel()

	spec := FormatSpec{
		Raw:          "{RecordType.RecordTypeCode}-{YY}{MM}{DD}-{00}",
		CounterWidth: 2,
		Tokens: []Token{
			{
				Kind:   TokenKindRecordType,
				Object: "RecordType",
				Field:  "RecordTypeCode",
				RefKey: "{RecordType.RecordTypeCode}",
			},
			{Kind: TokenKindLiteral, Lit: "-"},
			{Kind: TokenKindDate, DateName: "YY"},
			{Kind: TokenKindDate, DateName: "MM"},
			{Kind: TokenKindDate, DateName: "DD"},
			{Kind: TokenKindLiteral, Lit: "-"},
			{Kind: TokenKindCounter, Width: 2},
		},
	}
	assignedAt := time.Date(2026, 4, 17, 9, 0, 0, 0, time.UTC)
	cross := map[string]any{
		"{RecordType.RecordTypeCode}": "CSA",
	}

	got, err := Render(spec, assignedAt, 1, cross)
	require.NoError(t, err)
	assert.Equal(t, "CSA-260417-01", got)
}

// TestRender_PadsCounterToDeclaredWidth — the counter must always be
// left-padded with zeros to the declared width, even when the value
// has fewer digits. This guards the `%0*d` formatter in PRD §5.5.
func TestRender_PadsCounterToDeclaredWidth(t *testing.T) {
	t.Parallel()

	spec := FormatSpec{
		Raw:          "{000000}",
		CounterWidth: 6,
		Tokens: []Token{
			{Kind: TokenKindCounter, Width: 6},
		},
	}
	assignedAt := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)

	got, err := Render(spec, assignedAt, 42, nil)
	require.NoError(t, err)
	assert.Equal(t, "000042", got)
}
