namespace BudgetApp.Application.Services;

using BudgetApp.Domain.Interfaces;
using BudgetApp.Domain.Models;

/// <summary>
/// Application service for budget operations.
/// </summary>
public class BudgetService
{
    private readonly IBudgetRepository _budgetRepository;

    public BudgetService(IBudgetRepository budgetRepository)
    {
        _budgetRepository = budgetRepository ?? throw new ArgumentNullException(nameof(budgetRepository));
    }

    /// <summary>
    /// Creates a new base budget.
    /// </summary>
    public async Task<BaseBudget> CreateBaseBudgetAsync(string name, Money initialBalance)
    {
        if (string.IsNullOrWhiteSpace(name))
            throw new ArgumentException("Name cannot be null or empty.", nameof(name));
        
        if (initialBalance == null)
            throw new ArgumentNullException(nameof(initialBalance));

        var budget = new BaseBudget(name, initialBalance);
        await _budgetRepository.SaveAsync(budget);
        return budget;
    }

    /// <summary>
    /// Creates a new goal-based budget.
    /// </summary>
    public async Task<GoalBudget> CreateGoalBudgetAsync(
        string name,
        Money initialBalance,
        Money goalAmount,
        DateTime targetDate,
        Money periodicContribution,
        TimeSpan contributionPeriod)
    {
        if (string.IsNullOrWhiteSpace(name))
            throw new ArgumentException("Name cannot be null or empty.", nameof(name));
        
        if (initialBalance == null)
            throw new ArgumentNullException(nameof(initialBalance));
        
        if (goalAmount == null)
            throw new ArgumentNullException(nameof(goalAmount));
        
        if (periodicContribution == null)
            throw new ArgumentNullException(nameof(periodicContribution));

        var budget = new GoalBudget(name, initialBalance, goalAmount, targetDate, periodicContribution, contributionPeriod);
        await _budgetRepository.SaveAsync(budget);
        return budget;
    }

    /// <summary>
    /// Gets a budget by ID.
    /// </summary>
    public async Task<Budget?> GetBudgetByIdAsync(Guid budgetId)
    {
        return await _budgetRepository.GetByIdAsync(budgetId);
    }

    /// <summary>
    /// Gets all budgets.
    /// </summary>
    public async Task<IEnumerable<Budget>> GetAllBudgetsAsync()
    {
        return await _budgetRepository.GetAllAsync();
    }

    /// <summary>
    /// Gets the balance of a specific budget.
    /// </summary>
    public async Task<Money?> GetBudgetBalanceAsync(Guid budgetId)
    {
        var budget = await _budgetRepository.GetByIdAsync(budgetId);
        return budget?.Balance;
    }

    /// <summary>
    /// Calculates the total balance across all budgets for a specific currency.
    /// </summary>
    public async Task<Money> GetTotalBudgetBalanceAsync(string currency)
    {
        var budgets = await _budgetRepository.GetAllAsync();
        var total = budgets
            .Where(b => b.Balance.Currency == currency)
            .Sum(b => b.Balance.Amount);

        return new Money(total, currency);
    }

    /// <summary>
    /// Deletes a budget.
    /// </summary>
    public async Task DeleteBudgetAsync(Guid budgetId)
    {
        await _budgetRepository.DeleteAsync(budgetId);
    }
}

