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

namespace Books.Api.EventFlow.Repositories;

public class AccountRepository(NpgsqlDataSource dataSource) : IAccountRepository
{
    private const string SelectColumns = """
        aggregate_id AS Id,
        company_id AS CompanyId,
        account_number AS AccountNumber,
        name AS Name,
        account_type AS AccountType,
        parent_id AS ParentId,
        description AS Description,
        vat_code_id AS VatCodeId,
        is_active AS IsActive,
        is_system_account AS IsSystemAccount,
        standard_account_number AS StandardAccountNumber,
        create_time AS CreatedAt,
        updated_time AS UpdatedAt
        """;

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

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

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

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

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

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

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

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

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

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

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

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

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

        var sql = $"""
            SELECT {SelectColumns}
            FROM account_read_models
            WHERE company_id = @CompanyId AND account_number = @AccountNumber
            """;

        return await connection.QuerySingleOrDefaultAsync<AccountReadModelDto>(sql, new { CompanyId = companyId, AccountNumber = accountNumber });
    }
}
