using Microsoft.EntityFrameworkCore;
using ROLAC.API.Data;
using ROLAC.API.DTOs.Expense;
using ROLAC.API.Entities;

namespace ROLAC.API.Services;

public class ExpenseCategoryService : IExpenseCategoryService
{
    private readonly AppDbContext _db;
    public ExpenseCategoryService(AppDbContext db) => _db = db;

    public async Task<List<ExpenseCategoryGroupDto>> GetAllAsync(bool includeInactive)
    {
        var groups = await _db.ExpenseCategoryGroups.AsNoTracking()
            .Where(g => includeInactive || g.IsActive)
            .OrderBy(g => g.SortOrder).ThenBy(g => g.Name_en)
            .ToListAsync();

        var subs = await _db.ExpenseSubCategories.AsNoTracking()
            .Where(s => includeInactive || s.IsActive)
            .OrderBy(s => s.SortOrder).ThenBy(s => s.Name_en)
            .ToListAsync();

        var lineCodes = await _db.Form990ExpenseLines.AsNoTracking()
            .ToDictionaryAsync(l => l.Id, l => l.LineCode);

        var boxCodes = await _db.Form1099Boxes.AsNoTracking()
            .ToDictionaryAsync(b => b.Id, b => b.BoxCode);

        return groups.Select(g => new ExpenseCategoryGroupDto
        {
            Id = g.Id, Name_en = g.Name_en, Name_zh = g.Name_zh,
            SortOrder = g.SortOrder, IsActive = g.IsActive,
            Form990LineId   = g.Form990LineId,
            Form990LineCode = g.Form990LineId.HasValue ? lineCodes.GetValueOrDefault(g.Form990LineId.Value) : null,
            Form1099BoxId   = g.Form1099BoxId,
            Form1099BoxCode = g.Form1099BoxId.HasValue ? boxCodes.GetValueOrDefault(g.Form1099BoxId.Value) : null,
            SubCategories = subs.Where(s => s.GroupId == g.Id).Select(s => new ExpenseSubCategoryDto
            {
                Id = s.Id, GroupId = s.GroupId, Name_en = s.Name_en, Name_zh = s.Name_zh,
                SortOrder = s.SortOrder, IsActive = s.IsActive,
                Form990LineId   = s.Form990LineId,
                Form990LineCode = s.Form990LineId.HasValue ? lineCodes.GetValueOrDefault(s.Form990LineId.Value) : null,
                Form1099BoxId   = s.Form1099BoxId,
                Form1099BoxCode = s.Form1099BoxId.HasValue ? boxCodes.GetValueOrDefault(s.Form1099BoxId.Value) : null,
            }).ToList(),
        }).ToList();
    }

    public async Task<int> CreateGroupAsync(CreateExpenseGroupRequest r)
    {
        var g = new ExpenseCategoryGroup { Name_en = r.Name_en, Name_zh = r.Name_zh, SortOrder = r.SortOrder, IsActive = true, Form990LineId = r.Form990LineId, Form1099BoxId = r.Form1099BoxId };
        _db.ExpenseCategoryGroups.Add(g);
        await _db.SaveChangesAsync();
        return g.Id;
    }

    public async Task UpdateGroupAsync(int id, UpdateExpenseGroupRequest r)
    {
        var g = await _db.ExpenseCategoryGroups.FindAsync(id)
            ?? throw new KeyNotFoundException($"ExpenseCategoryGroup {id} not found.");
        g.Name_en = r.Name_en; g.Name_zh = r.Name_zh; g.SortOrder = r.SortOrder; g.IsActive = r.IsActive; g.Form990LineId = r.Form990LineId; g.Form1099BoxId = r.Form1099BoxId;
        await _db.SaveChangesAsync();
    }

    public async Task DeactivateGroupAsync(int id)
    {
        var g = await _db.ExpenseCategoryGroups.FindAsync(id)
            ?? throw new KeyNotFoundException($"ExpenseCategoryGroup {id} not found.");
        g.IsActive = false;
        await _db.SaveChangesAsync();
    }

    public async Task<int> CreateSubCategoryAsync(CreateExpenseSubCategoryRequest r)
    {
        var exists = await _db.ExpenseCategoryGroups.AnyAsync(g => g.Id == r.GroupId);
        if (!exists) throw new KeyNotFoundException($"ExpenseCategoryGroup {r.GroupId} not found.");
        var s = new ExpenseSubCategory { GroupId = r.GroupId, Name_en = r.Name_en, Name_zh = r.Name_zh, SortOrder = r.SortOrder, IsActive = true, Form990LineId = r.Form990LineId, Form1099BoxId = r.Form1099BoxId };
        _db.ExpenseSubCategories.Add(s);
        await _db.SaveChangesAsync();
        return s.Id;
    }

    public async Task UpdateSubCategoryAsync(int id, UpdateExpenseSubCategoryRequest r)
    {
        var s = await _db.ExpenseSubCategories.FindAsync(id)
            ?? throw new KeyNotFoundException($"ExpenseSubCategory {id} not found.");
        s.GroupId = r.GroupId; s.Name_en = r.Name_en; s.Name_zh = r.Name_zh; s.SortOrder = r.SortOrder; s.IsActive = r.IsActive; s.Form990LineId = r.Form990LineId; s.Form1099BoxId = r.Form1099BoxId;
        await _db.SaveChangesAsync();
    }

    public async Task DeactivateSubCategoryAsync(int id)
    {
        var s = await _db.ExpenseSubCategories.FindAsync(id)
            ?? throw new KeyNotFoundException($"ExpenseSubCategory {id} not found.");
        s.IsActive = false;
        await _db.SaveChangesAsync();
    }
}
