using Books.Api.EventFlow.ReadModels;
using Books.Api.EventFlow.Repositories;

namespace Books.Api.AiBookkeeper;

/// <summary>
/// Maps standard account numbers to company-specific accounts.
/// Uses the standardAccountNumber field on accounts for matching.
/// </summary>
public class AccountMappingService(
    IAccountRepository accountRepository,
    ILogger<AccountMappingService> logger) : IAccountMappingService
{
    public async Task<AccountReadModelDto?> FindByStandardAccountNumberAsync(
        string companyId,
        string standardAccountNumber,
        CancellationToken cancellationToken = default)
    {
        if (string.IsNullOrWhiteSpace(standardAccountNumber))
        {
            return null;
        }

        var accounts = await accountRepository.GetActiveByCompanyIdAsync(companyId, cancellationToken);

        // First, try exact match on standardAccountNumber
        var exactMatch = accounts.FirstOrDefault(a =>
            a.StandardAccountNumber != null &&
            a.StandardAccountNumber.Equals(standardAccountNumber, StringComparison.OrdinalIgnoreCase));

        if (exactMatch != null)
        {
            logger.LogDebug(
                "Found exact match for standard account {StandardAccount}: {AccountNumber} - {AccountName}",
                standardAccountNumber, exactMatch.AccountNumber, exactMatch.Name);
            return exactMatch;
        }

        // Try prefix match (standard account numbers can be hierarchical)
        var prefixMatch = accounts
            .Where(a =>
                a.StandardAccountNumber != null &&
                standardAccountNumber.StartsWith(a.StandardAccountNumber, StringComparison.OrdinalIgnoreCase))
            .OrderByDescending(a => a.StandardAccountNumber?.Length ?? 0)
            .FirstOrDefault();

        if (prefixMatch != null)
        {
            logger.LogDebug(
                "Found prefix match for standard account {StandardAccount}: {AccountNumber} - {AccountName}",
                standardAccountNumber, prefixMatch.AccountNumber, prefixMatch.Name);
            return prefixMatch;
        }

        logger.LogDebug("No match found for standard account {StandardAccount}", standardAccountNumber);
        return null;
    }

    public async Task<List<MappedSuggestedLine>> MapSuggestedLinesAsync(
        string companyId,
        List<SuggestedLine> suggestedLines,
        CancellationToken cancellationToken = default)
    {
        var accounts = await accountRepository.GetActiveByCompanyIdAsync(companyId, cancellationToken);

        // Index accounts by both StandardAccountNumber and direct AccountNumber
        var accountsByStandardNumber = accounts
            .Where(a => !string.IsNullOrEmpty(a.StandardAccountNumber))
            .GroupBy(a => a.StandardAccountNumber!)
            .ToDictionary(g => g.Key, g => g.First(), StringComparer.OrdinalIgnoreCase);

        var accountsByNumber = accounts
            .ToDictionary(a => a.AccountNumber, a => a, StringComparer.OrdinalIgnoreCase);

        var result = new List<MappedSuggestedLine>();

        foreach (var line in suggestedLines)
        {
            AccountReadModelDto? mappedAccount = null;

            if (!string.IsNullOrEmpty(line.StandardAccountNumber))
            {
                // First, try direct account number match (AI service returns company account numbers)
                if (accountsByNumber.TryGetValue(line.StandardAccountNumber, out var directMatch))
                {
                    mappedAccount = directMatch;
                }
                // Then try Erhvervsstyrelsen standard account number match
                else if (accountsByStandardNumber.TryGetValue(line.StandardAccountNumber, out var exactMatch))
                {
                    mappedAccount = exactMatch;
                }
                else
                {
                    // Try prefix match on standard numbers
                    mappedAccount = accountsByStandardNumber
                        .Where(kvp => line.StandardAccountNumber.StartsWith(kvp.Key, StringComparison.OrdinalIgnoreCase))
                        .OrderByDescending(kvp => kvp.Key.Length)
                        .Select(kvp => kvp.Value)
                        .FirstOrDefault();
                }
            }

            result.Add(new MappedSuggestedLine
            {
                Original = line,
                MappedAccount = mappedAccount
            });

            if (mappedAccount != null)
            {
                logger.LogDebug(
                    "Mapped standard account {StandardAccount} to {AccountNumber} - {AccountName}",
                    line.StandardAccountNumber, mappedAccount.AccountNumber, mappedAccount.Name);
            }
            else
            {
                logger.LogDebug(
                    "Could not map standard account {StandardAccount}",
                    line.StandardAccountNumber);
            }
        }

        return result;
    }
}
