using CashLineERP.Application.Finance;
using CashLineERP.Domain.Finance;

namespace CashLineERP.Infrastructure.Persistence.Repositories;

public sealed class CashAccountRepository : ICashAccountRepository
{
    private readonly CashLineErpDbContext _dbContext;

    public CashAccountRepository(CashLineErpDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public IReadOnlyCollection<CashAccountView> GetCashAccounts() =>
        _dbContext.Set<CashAccount>()
            .OrderBy(account => account.AccountCode)
            .ToArray()
            .Select(Map)
            .ToArray();

    public CashAccountView? GetCashAccount(long cashAccountId)
    {
        var account = _dbContext.Set<CashAccount>().SingleOrDefault(existingAccount => existingAccount.Id == cashAccountId);
        return account is null ? null : Map(account);
    }

    public CashAccountView Create(CreateCashAccountCommand command)
    {
        if (string.IsNullOrWhiteSpace(command.AccountCode))
        {
            throw new InvalidOperationException("Cash account code is required.");
        }

        if (string.IsNullOrWhiteSpace(command.AccountName))
        {
            throw new InvalidOperationException("Cash account name is required.");
        }

        if (string.IsNullOrWhiteSpace(command.AccountType))
        {
            throw new InvalidOperationException("Cash account type is required.");
        }

        if (_dbContext.Set<CashAccount>().Any(account => account.AccountCode == command.AccountCode.Trim().ToUpperInvariant()))
        {
            throw new InvalidOperationException($"Cash account code {command.AccountCode} already exists.");
        }

        var account = new CashAccount(
            accountCode: command.AccountCode.Trim().ToUpperInvariant(),
            accountName: command.AccountName.Trim(),
            accountType: command.AccountType.Trim().ToUpperInvariant(),
            currencyCode: command.CurrencyCode.Trim().ToUpperInvariant(),
            branchId: command.BranchId,
            openingBalanceAmount: command.OpeningBalanceAmount);

        _dbContext.Add(account);
        _dbContext.SaveChanges();
        return GetCashAccount(account.Id)!;
    }

    private static CashAccountView Map(CashAccount account) =>
        new(
            account.Id,
            account.AccountCode,
            account.AccountName,
            account.AccountType,
            account.CurrencyCode,
            account.BranchId,
            account.IsActive,
            account.OpeningBalanceAmount,
            account.CurrentBalanceAmount);
}
