package orm

import (
	"fmt"
	"strings"
	"time"
)

// NextByCode generates the next value for a sequence identified by its code.
// Mirrors: odoo/addons/base/models/ir_sequence.py IrSequence.next_by_code()
//
// Uses PostgreSQL FOR UPDATE to ensure atomic increment under concurrency.
// Format: prefix + LPAD(number, padding, '0') + suffix
// Supports date interpolation in prefix/suffix: %(year)s, %(month)s, %(day)s
func NextByCode(env *Environment, code string) (string, error) {
	var id int64
	var prefix, suffix string
	var numberNext, numberIncrement, padding int

	err := env.tx.QueryRow(env.ctx, `
		SELECT id, COALESCE(prefix, ''), COALESCE(suffix, ''), number_next, number_increment, padding
		FROM ir_sequence
		WHERE code = $1 AND active = true
		ORDER BY id
		LIMIT 1
		FOR UPDATE
	`, code).Scan(&id, &prefix, &suffix, &numberNext, &numberIncrement, &padding)

	if err != nil {
		return "", fmt.Errorf("orm: sequence %q not found: %w", code, err)
	}

	// Format the sequence value
	result := FormatSequence(prefix, suffix, numberNext, padding)

	// Increment for next call
	_, err = env.tx.Exec(env.ctx, `
		UPDATE ir_sequence SET number_next = number_next + $1 WHERE id = $2
	`, numberIncrement, id)
	if err != nil {
		return "", fmt.Errorf("orm: sequence %q increment failed: %w", code, err)
	}

	return result, nil
}

// FormatSequence formats a sequence number with prefix, suffix, and zero-padding.
func FormatSequence(prefix, suffix string, number, padding int) string {
	prefix = InterpolateDate(prefix)
	suffix = InterpolateDate(suffix)

	numStr := fmt.Sprintf("%d", number)
	if padding > 0 && len(numStr) < padding {
		numStr = strings.Repeat("0", padding-len(numStr)) + numStr
	}

	return prefix + numStr + suffix
}

// InterpolateDate replaces Odoo-style date placeholders in a string.
// Supports: %(year)s, %(month)s, %(day)s, %(y)s (2-digit year)
func InterpolateDate(s string) string {
	now := time.Now()
	s = strings.ReplaceAll(s, "%(year)s", now.Format("2006"))
	s = strings.ReplaceAll(s, "%(y)s", now.Format("06"))
	s = strings.ReplaceAll(s, "%(month)s", now.Format("01"))
	s = strings.ReplaceAll(s, "%(day)s", now.Format("02"))
	return s
}
