from django.utils.translation import gettext as _
from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from .models import Account, JournalEntry, JournalItem
from .forms import AccountForm, JournalEntryForm
from django.db.models import Sum, Q
from django.utils import timezone
from datetime import datetime
from django.db import transaction
import json

@login_required
def accounting_dashboard(request):
    total_assets = sum(acc.balance for acc in Account.objects.filter(account_type='asset'))
    total_liabilities = sum(acc.balance for acc in Account.objects.filter(account_type='liability'))
    total_equity = sum(acc.balance for acc in Account.objects.filter(account_type='equity'))
    
    # Revenue and Expenses for current month
    month_start = timezone.now().replace(day=1, hour=0, minute=0, second=0, microsecond=0)
    
    revenue_items = JournalItem.objects.filter(
        account__account_type='income',
        entry__date__gte=month_start
    )
    monthly_revenue = (revenue_items.filter(type='credit').aggregate(total=Sum('amount'))['total'] or 0) - \
                      (revenue_items.filter(type='debit').aggregate(total=Sum('amount'))['total'] or 0)

    expense_items = JournalItem.objects.filter(
        account__account_type='expense',
        entry__date__gte=month_start
    )
    monthly_expense = (expense_items.filter(type='debit').aggregate(total=Sum('amount'))['total'] or 0) - \
                      (expense_items.filter(type='credit').aggregate(total=Sum('amount'))['total'] or 0)

    context = {
        'total_assets': total_assets,
        'total_liabilities': total_liabilities,
        'total_equity': total_equity,
        'monthly_revenue': monthly_revenue,
        'monthly_expense': monthly_expense,
        'net_profit': monthly_revenue - monthly_expense,
        'recent_entries': JournalEntry.objects.order_by('-date', '-id')[:10]
    }
    return render(request, 'accounting/dashboard.html', context)

@login_required
def chart_of_accounts(request):
    accounts = Account.objects.all().order_by('code')
    return render(request, 'accounting/chart_of_accounts.html', {'accounts': accounts})

@login_required
def account_create_update(request, pk=None):
    if pk:
        account = get_object_or_404(Account, pk=pk)
    else:
        account = None
    
    if request.method == 'POST':
        form = AccountForm(request.POST, instance=account)
        if form.is_valid():
            form.save()
            messages.success(request, _("Account saved successfully."))
            return redirect('chart_of_accounts')
    else:
        form = AccountForm(instance=account)
    
    return render(request, 'accounting/account_form.html', {'form': form, 'account': account})

@login_required
def journal_entries(request):
    entries = JournalEntry.objects.all().order_by('-date', '-id')
    return render(request, 'accounting/journal_entries.html', {'entries': entries})

@login_required
def manual_journal_entry(request):
    accounts = Account.objects.filter(is_active=True).order_by('code')
    
    if request.method == 'POST':
        form = JournalEntryForm(request.POST)
        # Manual journal entry requires at least two items and they must balance
        account_ids = request.POST.getlist('account[]')
        types = request.POST.getlist('type[]')
        amounts = request.POST.getlist('amount[]')
        
        if form.is_valid():
            try:
                with transaction.atomic():
                    entry = form.save()
                    total_debit = 0
                    total_credit = 0
                    
                    for i in range(len(account_ids)):
                        acc_id = account_ids[i]
                        item_type = types[i]
                        amount = float(amounts[i])
                        
                        if amount <= 0: continue
                        
                        JournalItem.objects.create(
                            entry=entry,
                            account_id=acc_id,
                            type=item_type,
                            amount=amount
                        )
                        
                        if item_type == 'debit':
                            total_debit += amount
                        else:
                            total_credit += amount
                    
                    if round(total_debit, 3) != round(total_credit, 3):
                        raise Exception(f"Journal entry does not balance. Total Debit: {total_debit}, Total Credit: {total_credit}")
                    
                    if total_debit == 0:
                        raise Exception("Journal entry must have at least one debit and one credit.")

                    messages.success(request, _("Manual journal entry created successfully."))
                    return redirect('journal_entries')
            except Exception as e:
                messages.error(request, str(e))
    else:
        form = JournalEntryForm()

    return render(request, 'accounting/journal_entry_form.html', {
        'form': form,
        'accounts': accounts
    })

@login_required
def account_ledger(request, account_id):
    account = get_object_or_404(Account, id=account_id)
    items = JournalItem.objects.filter(account=account).order_by('entry__date', 'entry__id')
    
    # Calculate running balance
    running_balance = 0
    ledger_items = []
    for item in items:
        if account.account_type in ['asset', 'expense']:
            change = item.amount if item.type == 'debit' else -item.amount
        else:
            change = item.amount if item.type == 'credit' else -item.amount
        running_balance += change
        ledger_items.append({
            'item': item,
            'balance': running_balance
        })
        
    return render(request, 'accounting/account_ledger.html', {
        'account': account,
        'ledger_items': ledger_items,
        'total_balance': running_balance
    })

@login_required
def trial_balance(request):
    accounts = Account.objects.all().order_by('code')
    trial_data = []
    total_debit = 0
    total_credit = 0
    
    for acc in accounts:
        items = acc.journal_items.all()
        debits = items.filter(type='debit').aggregate(total=Sum('amount'))['total'] or 0
        credits = items.filter(type='credit').aggregate(total=Sum('amount'))['total'] or 0
        
        if debits > 0 or credits > 0:
            trial_data.append({
                'account': acc,
                'debit': debits,
                'credit': credits
            })
            total_debit += debits
            total_credit += credits
            
    return render(request, 'accounting/trial_balance.html', {
        'trial_data': trial_data,
        'total_debit': total_debit,
        'total_credit': total_credit
    })

@login_required
def balance_sheet(request):
    assets = Account.objects.filter(account_type='asset')
    liabilities = Account.objects.filter(account_type='liability')
    equity = Account.objects.filter(account_type='equity')
    
    # Include Net Income in Equity
    revenue = JournalItem.objects.filter(account__account_type='income').aggregate(
        cr=Sum('amount', filter=Q(type='credit')),
        dr=Sum('amount', filter=Q(type='debit'))
    )
    net_revenue = (revenue['cr'] or 0) - (revenue['dr'] or 0)
    
    expenses = JournalItem.objects.filter(account__account_type='expense').aggregate(
        dr=Sum('amount', filter=Q(type='debit')),
        cr=Sum('amount', filter=Q(type='credit'))
    )
    net_expenses = (expenses['dr'] or 0) - (expenses['cr'] or 0)
    net_income = net_revenue - net_expenses

    asset_total = sum(acc.balance for acc in assets)
    liability_total = sum(acc.balance for acc in liabilities)
    equity_total = sum(acc.balance for acc in equity) + net_income
    
    return render(request, 'accounting/balance_sheet.html', {
        'assets': assets,
        'liabilities': liabilities,
        'equity': equity,
        'net_income': net_income,
        'asset_total': asset_total,
        'liability_total': liability_total,
        'equity_total': equity_total,
        'date': timezone.now()
    })

@login_required
def profit_loss(request):
    revenue_accounts = Account.objects.filter(account_type='income')
    expense_accounts = Account.objects.filter(account_type='expense')
    
    revenue_total = sum(acc.balance for acc in revenue_accounts)
    expense_total = sum(acc.balance for acc in expense_accounts)
    
    return render(request, 'accounting/profit_loss.html', {
        'revenue_accounts': revenue_accounts,
        'expense_accounts': expense_accounts,
        'revenue_total': revenue_total,
        'expense_total': expense_total,
        'net_profit': revenue_total - expense_total,
        'date': timezone.now()
    })