using Dapper;
using Npgsql;

namespace Books.Api.Invoicing.Services;

/// <summary>
/// PostgreSQL implementation of invoice number generation.
/// Uses database-level locking via UPSERT to ensure unique sequential numbers.
/// </summary>
public class InvoiceNumberService(NpgsqlDataSource dataSource) : IInvoiceNumberService
{
    public async Task<string> GetNextCustomerNumberAsync(string companyId, CancellationToken cancellationToken = default)
    {
        var number = await GetNextNumberAsync(companyId, "customer", "", cancellationToken);
        return number.ToString("D4"); // 0001, 0002, etc.
    }

    public async Task<string> GetNextInvoiceNumberAsync(string companyId, int year, CancellationToken cancellationToken = default)
    {
        var number = await GetNextNumberAsync(companyId, $"invoice-{year}", $"INV-{year}-", cancellationToken);
        return $"INV-{year}-{number:D4}"; // INV-2024-0001
    }

    public async Task<string> GetNextCreditNoteNumberAsync(string companyId, int year, CancellationToken cancellationToken = default)
    {
        var number = await GetNextNumberAsync(companyId, $"creditnote-{year}", $"CN-{year}-", cancellationToken);
        return $"CN-{year}-{number:D4}"; // CN-2024-0001
    }

    private async Task<int> GetNextNumberAsync(
        string companyId,
        string sequenceKey,
        string prefix,
        CancellationToken cancellationToken)
    {
        await using var connection = await dataSource.OpenConnectionAsync(cancellationToken);

        // Use UPSERT with RETURNING to atomically get and increment the sequence
        const string sql = """
            INSERT INTO number_series (company_id, sequence_key, last_number, prefix, updated_at)
            VALUES (@CompanyId, @SequenceKey, 1, @Prefix, NOW())
            ON CONFLICT (company_id, sequence_key)
            DO UPDATE SET
                last_number = number_series.last_number + 1,
                updated_at = NOW()
            RETURNING last_number
            """;

        return await connection.QuerySingleAsync<int>(
            sql,
            new { CompanyId = companyId, SequenceKey = sequenceKey, Prefix = prefix });
    }
}
