using DoliMiddlewareApi.Dtos.command;
using DoliMiddlewareApi.Dtos.Dolibarr;
using DoliMiddlewareApi.Dtos.query;
using DoliMiddlewareApi.Exceptions;
using DoliMiddlewareApi.Mappers;
using DoliMiddlewareApi.Services.Clients;

namespace DoliMiddlewareApi.Services;

public class BankService(IDolibarrApiClient apiClient)
{
    public async Task<List<BankAccountDto>> GetAccountsAsync()
    {
        var accounts = await apiClient.GetCollectionAsync<BankAccountResponse>("bankaccounts?sortfield=t.rowid&sortorder=ASC&limit=100");
        return accounts.Select(BankMapper.MapToAccountDto).ToList();
    }

    public async Task<BankAccountDto> GetAccountAsync(int id)
    {
        var account = await apiClient.GetResourceAsync<BankAccountResponse>($"bankaccounts/{id}");
        return BankMapper.MapToAccountDto(account);
    }

    public async Task<double> GetBalanceAsync(int id)
    {
        var result = await apiClient.GetResourceAsync<BankAccountResponse>($"bankaccounts/{id}/balance");
        return result.solde ?? 0;
    }

    public async Task<List<BankLineDto>> GetMovementsAsync(int limit = 20)
    {
        // Fetch all accounts first, then their lines in parallel
        List<BankAccountResponse> accounts;
        try
        {
            accounts = await apiClient.GetCollectionAsync<BankAccountResponse>("bankaccounts?sortfield=t.rowid&sortorder=ASC&limit=100");
        }
        catch (ApiException)
        {
            return [];
        }

        if (accounts.Count == 0)
            return [];

        var lineTasks = accounts
            .Where(a => int.TryParse(a.id, out _))
            .Select(async a =>
            {
                var accountId = int.Parse(a.id!);
                try
                {
                    var lines = await apiClient.GetCollectionAsync<BankLineResponse>($"bankaccounts/{accountId}/lines");
                    return lines.Select(l => BankMapper.MapToLineDto(l, accountId));
                }
                catch
                {
                    return Enumerable.Empty<BankLineDto>();
                }
            });

        var results = await Task.WhenAll(lineTasks);

        return results
            .SelectMany(lines => lines)
            .OrderByDescending(l => l.Date ?? l.DateValue ?? DateTime.MinValue)
            .Take(limit)
            .ToList();
    }

    public async Task<List<BankLineDto>> GetAccountLinesAsync(int accountId)
    {
        var lines = await apiClient.GetCollectionAsync<BankLineResponse>($"bankaccounts/{accountId}/lines");
        return lines.Select(l => BankMapper.MapToLineDto(l, accountId)).ToList();
    }

    public async Task UpdateAccountAsync(int id, UpdateBankAccountDto dto)
    {
        var requestBody = new Dictionary<string, object?>();
        if (dto.Label        != null) requestBody["label"]         = dto.Label;
        if (dto.Ref          != null) requestBody["ref"]           = dto.Ref;
        if (dto.Type         != null) requestBody["type"]          = dto.Type;
        if (dto.CurrencyCode != null) requestBody["currency_code"] = dto.CurrencyCode;
        if (dto.CountryId    != null) requestBody["country_id"]    = dto.CountryId;
        if (dto.AccountNumber!= null) requestBody["number"]        = dto.AccountNumber;
        if (dto.Iban         != null) requestBody["iban_prefix"]   = dto.Iban;
        if (dto.Bic          != null) requestBody["bic"]           = dto.Bic;
        if (dto.Bank         != null) requestBody["bank"]          = dto.Bank;
        await apiClient.PutAsync($"bankaccounts/{id}", requestBody);
    }

    public async Task<int> CreateAccountAsync(CreateBankAccountDto dto)
    {
        var requestBody = new Dictionary<string, object?>
        {
            ["ref"]           = dto.Ref,
            ["label"]         = dto.Label,
            ["type"]          = dto.Type,
            ["currency_code"] = dto.CurrencyCode,
            ["country_id"]    = dto.CountryId,
            ["number"]        = dto.AccountNumber,
            ["iban_prefix"]   = dto.Iban,
            ["bic"]           = dto.Bic,
            ["bank"]          = dto.Bank,
            ["clos"]          = 0,
        };

        var response = await apiClient.PostAsync("bankaccounts", requestBody);
        return int.TryParse(response, out var id) ? id : 0;
    }
}
