// Package seq implements atomic per-day/global document sequences.
// Replaces Laravel COUNT()+1 hooks (migration-plan.md §3). A single table
// document_number_sequences(prefix, doc_date, counter) gives atomic counting
// via INSERT ... ON CONFLICT ... DO UPDATE ... RETURNING counter.
package seq

import (
	"context"
	"fmt"
	"time"

	"github.com/jackc/pgx/v5"
)

// Querier is *pgxpool.Pool or a pgx.Tx.
type Querier interface {
	QueryRow(ctx context.Context, sql string, args ...any) pgx.Row
}

// Store is the atomic-counter dependency. Implemented by Repository.
type Store interface {
	Next(ctx context.Context, q Querier, prefix string, date time.Time) (int64, error)
}

// Next returns the next counter for (prefix, date), starting at 1, in one
// atomic upsert. date is the local (Asia/Jakarta) date of the document.
func Next(ctx context.Context, q Querier, prefix string, date time.Time) (int64, error) {
	var n int64
	err := q.QueryRow(ctx, `
		INSERT INTO document_number_sequences (prefix, doc_date, counter)
		VALUES ($1, $2::date, 1)
		ON CONFLICT (prefix, doc_date)
		DO UPDATE SET counter = document_number_sequences.counter + 1
		RETURNING counter`,
		prefix, date,
	).Scan(&n)
	if err != nil {
		return 0, fmt.Errorf("seq %s: %w", prefix, err)
	}
	return n, nil
}

// MonthStart returns the first day of the month containing t, in the caller's
// location. Passing it to Next scopes the counter by document type + calendar
// month (business-rules.md §17): each month restarts the counter and month
// rollover is automatic because doc_date changes. Asia/Jakarta callers should
// pass time.Now().In(loc) so the month boundary follows that zone.
func MonthStart(t time.Time) time.Time {
	y, m, _ := t.Date()
	return time.Date(y, m, 1, 0, 0, 0, 0, t.Location())
}
