using Books.Api.Domain.Customers;
using Books.Api.EventFlow.ReadModels;
using Dapper;
using Npgsql;

namespace Books.Api.EventFlow.Repositories;

public class CustomerRepository(NpgsqlDataSource dataSource) : ICustomerRepository
{
    private const string SelectColumns = """
        aggregate_id AS Id,
        company_id AS CompanyId,
        customer_number AS CustomerNumber,
        customer_type AS CustomerType,
        name AS Name,
        cvr AS Cvr,
        address AS Address,
        postal_code AS PostalCode,
        city AS City,
        country AS Country,
        email AS Email,
        phone AS Phone,
        payment_terms_days AS PaymentTermsDays,
        default_revenue_account_id AS DefaultRevenueAccountId,
        sub_ledger_account_id AS SubLedgerAccountId,
        is_active AS IsActive,
        create_time AS CreatedAt,
        updated_time AS UpdatedAt
        """;

    public async Task<CustomerReadModelDto?> GetByIdAsync(string id, CancellationToken cancellationToken = default)
    {
        await using var connection = await dataSource.OpenConnectionAsync(cancellationToken);

        var sql = $"""
            SELECT {SelectColumns}
            FROM customer_read_models
            WHERE aggregate_id = @Id
            """;

        return await connection.QuerySingleOrDefaultAsync<CustomerReadModelDto>(sql, new { Id = id });
    }

    public async Task<IEnumerable<CustomerReadModelDto>> GetByIdsAsync(List<CustomerId> ids, CancellationToken cancellationToken = default)
    {
        await using var connection = await dataSource.OpenConnectionAsync(cancellationToken);

        var sql = $"""
            SELECT {SelectColumns}
            FROM customer_read_models
            WHERE aggregate_id = ANY(@Ids)
            ORDER BY customer_number
            """;

        return await connection.QueryAsync<CustomerReadModelDto>(sql, new { Ids = ids.Select(i => i.Value).ToArray() });
    }

    public async Task<IReadOnlyList<CustomerReadModelDto>> GetByCompanyIdAsync(string companyId, CancellationToken cancellationToken = default)
    {
        await using var connection = await dataSource.OpenConnectionAsync(cancellationToken);

        var sql = $"""
            SELECT {SelectColumns}
            FROM customer_read_models
            WHERE company_id = @CompanyId
            ORDER BY customer_number
            """;

        var result = await connection.QueryAsync<CustomerReadModelDto>(sql, new { CompanyId = companyId });
        return result.ToList();
    }

    public async Task<IReadOnlyList<CustomerReadModelDto>> GetActiveByCompanyIdAsync(string companyId, CancellationToken cancellationToken = default)
    {
        await using var connection = await dataSource.OpenConnectionAsync(cancellationToken);

        var sql = $"""
            SELECT {SelectColumns}
            FROM customer_read_models
            WHERE company_id = @CompanyId AND is_active = TRUE
            ORDER BY customer_number
            """;

        var result = await connection.QueryAsync<CustomerReadModelDto>(sql, new { CompanyId = companyId });
        return result.ToList();
    }

    public async Task<CustomerReadModelDto?> GetByCompanyAndNumberAsync(string companyId, string customerNumber, CancellationToken cancellationToken = default)
    {
        await using var connection = await dataSource.OpenConnectionAsync(cancellationToken);

        var sql = $"""
            SELECT {SelectColumns}
            FROM customer_read_models
            WHERE company_id = @CompanyId AND customer_number = @CustomerNumber
            """;

        return await connection.QuerySingleOrDefaultAsync<CustomerReadModelDto>(sql, new { CompanyId = companyId, CustomerNumber = customerNumber });
    }

    public async Task<CustomerReadModelDto?> GetByCvrAsync(string cvr, CancellationToken cancellationToken = default)
    {
        await using var connection = await dataSource.OpenConnectionAsync(cancellationToken);

        var sql = $"""
            SELECT {SelectColumns}
            FROM customer_read_models
            WHERE cvr = @Cvr
            """;

        return await connection.QuerySingleOrDefaultAsync<CustomerReadModelDto>(sql, new { Cvr = cvr });
    }
}
