"""
Views for the accounting module.
"""

from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.db.models import Q, Sum, Count, F
from django.core.paginator import Paginator
from django.http import JsonResponse
from django.utils import timezone
from django.db import transaction
from django.core.exceptions import ValidationError
from decimal import Decimal
from datetime import date

from .models import Account, JournalEntry, JournalEntryLine, GeneralLedger, FiscalPeriod
from .forms import (
    AccountForm, AccountFilterForm, 
    JournalEntryForm, JournalEntryLineInlineFormSet, JournalEntryFilterForm,
    FiscalPeriodForm
)
from .services.accounting_service import AccountingService
from users.decorators import staff_required


def apply_account_branch_filter(queryset, user):
    """
    Apply branch filtering to account-related querysets based on user permissions.
    For now, accounts are global, so this returns the queryset as-is.
    """
    return queryset


@login_required
@staff_required
def account_list(request):
    """
    List all accounts with filtering and search.
    Displays accounts in hierarchical tree structure with indentation.
    Performance: Uses select_related for foreign key queries (Requirement 20.1, 20.2)
    """
    # Get base queryset with optimized query using select_related for all foreign keys
    accounts_qs = Account.objects.select_related('parent_account', 'created_by').only(
        'id', 'code', 'name', 'account_type', 'subtype', 'is_active', 
        'is_system_account', 'created_at', 'parent_account__code', 
        'parent_account__name', 'created_by__username'
    ).all()
    
    # Initialize filter form
    filter_form = AccountFilterForm(request.GET)
    
    # Apply filters
    if filter_form.is_valid():
        # Search
        search = filter_form.cleaned_data.get('search')
        if search:
            accounts_qs = accounts_qs.filter(
                Q(code__icontains=search) |
                Q(name__icontains=search) |
                Q(description__icontains=search)
            )
        
        # Account type filter
        account_type = filter_form.cleaned_data.get('account_type')
        if account_type:
            accounts_qs = accounts_qs.filter(account_type=account_type)
        
        # Status filter
        status = filter_form.cleaned_data.get('status')
        if status == 'active':
            accounts_qs = accounts_qs.filter(is_active=True)
        elif status == 'inactive':
            accounts_qs = accounts_qs.filter(is_active=False)
        
        # Parent only filter
        parent_only = filter_form.cleaned_data.get('parent_only')
        if parent_only:
            accounts_qs = accounts_qs.filter(parent_account__isnull=True)
    
    # Order by code for hierarchical display
    accounts_qs = accounts_qs.order_by('code')
    
    # Build hierarchical structure with indentation levels
    hierarchical_accounts = []
    accounting_service = AccountingService()
    
    def add_account_with_children(account, level=0):
        """Recursively add account and its children with proper indentation."""
        # Calculate current balance
        try:
            balance = accounting_service.calculate_account_balance(
                account=account,
                as_of_date=date.today(),
                branch=None
            )
        except Exception:
            balance = Decimal('0.00')
        
        hierarchical_accounts.append({
            'account': account,
            'level': level,
            'balance': balance,
        })
        
        # Add children
        children = Account.objects.filter(parent_account=account).order_by('code')
        for child in children:
            add_account_with_children(child, level + 1)
    
    # If filtering is active, just show flat list
    if filter_form.is_valid() and any([
        filter_form.cleaned_data.get('search'),
        filter_form.cleaned_data.get('account_type'),
        filter_form.cleaned_data.get('status'),
        filter_form.cleaned_data.get('parent_only')
    ]):
        for account in accounts_qs:
            try:
                balance = accounting_service.calculate_account_balance(
                    account=account,
                    as_of_date=date.today(),
                    branch=None
                )
            except Exception:
                balance = Decimal('0.00')
            
            hierarchical_accounts.append({
                'account': account,
                'level': 0,
                'balance': balance,
            })
    else:
        # Build full hierarchy - start with top-level accounts
        top_level_accounts = accounts_qs.filter(parent_account__isnull=True)
        for account in top_level_accounts:
            add_account_with_children(account)
    
    # Calculate summary statistics
    total_accounts = Account.objects.count()
    active_accounts = Account.objects.filter(is_active=True).count()
    inactive_accounts = total_accounts - active_accounts
    
    # Count by type
    accounts_by_type = Account.objects.values('account_type').annotate(
        count=Count('id')
    ).order_by('account_type')
    
    # Pagination
    paginator = Paginator(hierarchical_accounts, 50)
    page_number = request.GET.get('page', 1)
    accounts_page = paginator.get_page(page_number)
    
    context = {
        'accounts': accounts_page,
        'filter_form': filter_form,
        'total_accounts': total_accounts,
        'active_accounts': active_accounts,
        'inactive_accounts': inactive_accounts,
        'accounts_by_type': accounts_by_type,
    }
    
    return render(request, 'accounting/accounts/list.html', context)


@login_required
@staff_required
def account_detail(request, account_id):
    """
    Display account details with balance and recent transactions.
    Performance: Uses select_related and prefetch_related for optimal queries (Requirement 20.1, 20.2)
    """
    account = get_object_or_404(
        Account.objects.select_related('parent_account', 'created_by').only(
            'id', 'code', 'name', 'account_type', 'subtype', 'description',
            'is_active', 'is_system_account', 'created_at', 'updated_at',
            'parent_account__code', 'parent_account__name', 'created_by__username'
        ),
        pk=account_id
    )
    
    # Get child accounts with limited fields
    child_accounts = Account.objects.filter(parent_account=account).only(
        'id', 'code', 'name', 'account_type', 'is_active'
    ).order_by('code')
    
    # Calculate current balance
    accounting_service = AccountingService()
    try:
        current_balance = accounting_service.calculate_account_balance(
            account=account,
            as_of_date=date.today(),
            branch=None
        )
    except Exception as e:
        current_balance = Decimal('0.00')
        messages.warning(request, f'Could not calculate balance: {str(e)}')
    
    # Get recent transactions from general ledger with optimized query
    # Uses select_related for all foreign keys (Requirement 20.1, 20.2)
    recent_transactions = GeneralLedger.objects.filter(
        account=account
    ).select_related(
        'journal_entry', 'branch', 'posted_by'
    ).order_by('-transaction_date', '-posted_at')[:20]
    
    # Calculate transaction summary using aggregate at database level (Requirement 20.1)
    transaction_summary = GeneralLedger.objects.filter(account=account).aggregate(
        total_debits=Sum('debit_amount'),
        total_credits=Sum('credit_amount'),
        transaction_count=Count('id')
    )
    
    # Get usage count (number of journal entries using this account)
    usage_count = JournalEntry.objects.filter(
        lines__account=account
    ).distinct().count()
    
    context = {
        'account': account,
        'child_accounts': child_accounts,
        'current_balance': current_balance,
        'recent_transactions': recent_transactions,
        'transaction_summary': transaction_summary,
        'usage_count': usage_count,
    }
    
    return render(request, 'accounting/accounts/detail.html', context)


@login_required
@staff_required
def account_create(request):
    """Create a new account."""
    if request.method == 'POST':
        form = AccountForm(request.POST)
        if form.is_valid():
            account = form.save(commit=False)
            account.created_by = request.user
            account.save()
            
            # Invalidate Chart of Accounts cache (Requirement 20.4)
            accounting_service = AccountingService()
            accounting_service.invalidate_chart_cache()
            
            messages.success(
                request,
                f'Account "{account.code} - {account.name}" created successfully.'
            )
            return redirect('accounting:account_detail', account_id=account.pk)
    else:
        form = AccountForm()
    
    context = {
        'form': form,
        'title': 'Create New Account',
        'submit_text': 'Create Account',
    }
    
    return render(request, 'accounting/accounts/form.html', context)


@login_required
@staff_required
def account_edit(request, account_id):
    """Edit an existing account."""
    account = get_object_or_404(Account, pk=account_id)
    
    # Check if account is a system account
    if account.is_system_account:
        messages.warning(
            request,
            'This is a system account. Editing is restricted to preserve system integrity.'
        )
    
    if request.method == 'POST':
        form = AccountForm(request.POST, instance=account)
        if form.is_valid():
            # Prevent editing code and type for system accounts
            if account.is_system_account:
                form.instance.code = account.code
                form.instance.account_type = account.account_type
            
            account = form.save()
            
            # Invalidate Chart of Accounts cache (Requirement 20.4)
            accounting_service = AccountingService()
            accounting_service.invalidate_chart_cache()
            
            messages.success(
                request,
                f'Account "{account.code} - {account.name}" updated successfully.'
            )
            return redirect('accounting:account_detail', account_id=account.pk)
    else:
        form = AccountForm(instance=account)
    
    context = {
        'form': form,
        'account': account,
        'title': f'Edit Account: {account.code} - {account.name}',
        'submit_text': 'Update Account',
    }
    
    return render(request, 'accounting/accounts/form.html', context)


@login_required
@staff_required
def account_toggle_status(request, account_id):
    """
    Toggle account activation/deactivation.
    This is typically done via AJAX.
    """
    account = get_object_or_404(Account, pk=account_id)
    
    # Check if account is a system account
    if account.is_system_account and account.is_active:
        messages.error(
            request,
            'Cannot deactivate a system account. System accounts are required for proper operation.'
        )
        return redirect('accounting:account_detail', account_id=account.pk)
    
    # Check if account has been used in any journal entries
    usage_count = JournalEntry.objects.filter(
        lines__account=account
    ).distinct().count()
    
    if usage_count > 0 and account.is_active:
        # Account has transactions, warn user
        messages.warning(
            request,
            f'This account has been used in {usage_count} journal entries. '
            f'Deactivating will prevent future use but preserve historical data.'
        )
    
    # Toggle status
    account.is_active = not account.is_active
    account.save()
    
    # Invalidate Chart of Accounts cache (Requirement 20.4)
    accounting_service = AccountingService()
    accounting_service.invalidate_chart_cache()
    
    status_text = 'activated' if account.is_active else 'deactivated'
    messages.success(
        request,
        f'Account "{account.code} - {account.name}" has been {status_text}.'
    )
    
    # Handle AJAX requests
    if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
        return JsonResponse({
            'success': True,
            'is_active': account.is_active,
            'message': f'Account {status_text} successfully.',
        })
    
    return redirect('accounting:account_detail', account_id=account.pk)



# ==================== Journal Entry Views ====================

@login_required
@staff_required
def journal_entry_list(request):
    """
    List all journal entries with filtering and pagination.
    Supports filtering by status, date range, and branch.
    Performance: Uses select_related and prefetch_related for optimal queries (Requirement 20.1, 20.2)
    """
    # Get base queryset with optimizations - use select_related for foreign keys
    # and prefetch_related for reverse foreign key (lines)
    entries_qs = JournalEntry.objects.select_related(
        'branch', 'created_by', 'posted_by', 'loan', 'expense'
    ).prefetch_related(
        'lines__account'
    ).only(
        'id', 'reference_number', 'transaction_date', 'description', 'status',
        'created_at', 'posted_at',
        'branch__name', 'created_by__username', 'posted_by__username',
        'loan__loan_number', 'expense__id'
    ).all()
    
    # Initialize filter form
    filter_form = JournalEntryFilterForm(request.GET, user=request.user)
    
    # Apply filters
    if filter_form.is_valid():
        # Status filter
        status = filter_form.cleaned_data.get('status')
        if status:
            entries_qs = entries_qs.filter(status=status)
        
        # Date range filter
        start_date = filter_form.cleaned_data.get('start_date')
        if start_date:
            entries_qs = entries_qs.filter(transaction_date__gte=start_date)
        
        end_date = filter_form.cleaned_data.get('end_date')
        if end_date:
            entries_qs = entries_qs.filter(transaction_date__lte=end_date)
        
        # Branch filter (enforce branch access control)
        branch = filter_form.cleaned_data.get('branch')
        if branch:
            entries_qs = entries_qs.filter(branch=branch)
    
    # Order by transaction date (newest first)
    entries_qs = entries_qs.order_by('-transaction_date', '-created_at')
    
    # Calculate summary statistics
    total_entries = JournalEntry.objects.count()
    draft_entries = JournalEntry.objects.filter(status='draft').count()
    posted_entries = JournalEntry.objects.filter(status='posted').count()
    reversed_entries = JournalEntry.objects.filter(status='reversed').count()
    
    # Pagination
    paginator = Paginator(entries_qs, 50)
    page_number = request.GET.get('page', 1)
    entries_page = paginator.get_page(page_number)
    
    # Calculate totals for each entry
    entries_with_totals = []
    for entry in entries_page:
        total_debit = sum(line.debit_amount for line in entry.lines.all())
        total_credit = sum(line.credit_amount for line in entry.lines.all())
        entries_with_totals.append({
            'entry': entry,
            'total_debit': total_debit,
            'total_credit': total_credit,
        })
    
    context = {
        'entries': entries_page,
        'entries_with_totals': entries_with_totals,
        'filter_form': filter_form,
        'total_entries': total_entries,
        'draft_entries': draft_entries,
        'posted_entries': posted_entries,
        'reversed_entries': reversed_entries,
    }
    
    return render(request, 'accounting/journal/list.html', context)


@login_required
@staff_required
def journal_entry_detail(request, reference_number):
    """
    Display journal entry details with all line items.
    Shows linked loan or expense if applicable.
    """
    entry = get_object_or_404(
        JournalEntry.objects.select_related(
            'branch', 'created_by', 'posted_by', 'loan', 'expense', 'reverses'
        ).prefetch_related('lines__account'),
        reference_number=reference_number
    )
    
    # Calculate totals
    total_debits = sum(line.debit_amount for line in entry.lines.all())
    total_credits = sum(line.credit_amount for line in entry.lines.all())
    
    # Check if this entry has been reversed
    reversal_entry = None
    if hasattr(entry, 'reversed_by') and entry.reversed_by:
        reversal_entry = entry.reversed_by
    
    context = {
        'entry': entry,
        'total_debits': total_debits,
        'total_credits': total_credits,
        'reversal_entry': reversal_entry,
    }
    
    return render(request, 'accounting/journal/detail.html', context)


@login_required
@staff_required
def journal_entry_create(request):
    """
    Create a new journal entry with dynamic line items.
    Saves as draft by default.
    """
    if request.method == 'POST':
        form = JournalEntryForm(request.POST, user=request.user)
        formset = JournalEntryLineInlineFormSet(request.POST)
        
        if form.is_valid() and formset.is_valid():
            try:
                with transaction.atomic():
                    # Create journal entry
                    entry = form.save(commit=False)
                    entry.created_by = request.user
                    entry.status = 'draft'
                    
                    # Validate fiscal period is open (Requirement 16.4)
                    period = FiscalPeriod.objects.filter(
                        start_date__lte=entry.transaction_date,
                        end_date__gte=entry.transaction_date
                    ).first()
                    
                    if period and period.status == 'closed':
                        messages.error(
                            request,
                            f'Cannot create journal entry. The fiscal period '
                            f'"{period.name}" is closed for {entry.transaction_date}.'
                        )
                        raise ValidationError('Fiscal period is closed')
                    
                    # Generate reference number
                    # Format: JE-YYYYMMDD-XXX
                    today = date.today()
                    date_prefix = today.strftime('%Y%m%d')
                    
                    # Get count of entries today
                    today_count = JournalEntry.objects.filter(
                        reference_number__startswith=f'JE-{date_prefix}'
                    ).count()
                    
                    entry.reference_number = f'JE-{date_prefix}-{today_count + 1:03d}'
                    entry.save()
                    
                    # Save formset with journal entry
                    formset.instance = entry
                    
                    # Set line numbers and validate accounts (Requirement 16.6)
                    line_number = 1
                    for line_form in formset:
                        if line_form.cleaned_data and not line_form.cleaned_data.get('DELETE', False):
                            line = line_form.save(commit=False)
                            
                            # Validate account is active
                            if not line.account.is_active:
                                messages.error(
                                    request,
                                    f'Cannot use inactive account: {line.account.code} - {line.account.name}'
                                )
                                raise ValidationError('Inactive account in journal entry')
                            
                            line.journal_entry = entry
                            line.line_number = line_number
                            line.save()
                            line_number += 1
                    
                    messages.success(
                        request,
                        f'Journal entry "{entry.reference_number}" created successfully as draft.'
                    )
                    return redirect('accounting:journal_entry_detail', reference_number=entry.reference_number)
            
            except ValidationError:
                # Error message already added above
                pass
            except Exception as e:
                messages.error(request, f'Error creating journal entry: {str(e)}')
        else:
            if formset.errors:
                messages.error(request, 'Please correct the errors in the journal entry lines.')
    else:
        form = JournalEntryForm(user=request.user)
        formset = JournalEntryLineInlineFormSet()
    
    context = {
        'form': form,
        'formset': formset,
        'title': 'Create Journal Entry',
    }
    
    return render(request, 'accounting/journal/create.html', context)


@login_required
@staff_required
def journal_entry_post(request, reference_number):
    """
    Post a draft journal entry to the general ledger.
    """
    entry = get_object_or_404(JournalEntry, reference_number=reference_number)
    
    # Validate that entry is in draft status (Requirement 16.8)
    if entry.status != 'draft':
        messages.error(
            request,
            f'Cannot post this journal entry. Only draft entries can be posted. '
            f'Current status: {entry.get_status_display()}'
        )
        return redirect('accounting:journal_entry_detail', reference_number=reference_number)
    
    # Validate fiscal period is open (Requirement 16.4)
    period = FiscalPeriod.objects.filter(
        start_date__lte=entry.transaction_date,
        end_date__gte=entry.transaction_date
    ).first()
    
    if period and period.status == 'closed':
        messages.error(
            request,
            f'Cannot post journal entry. The fiscal period "{period.name}" '
            f'is closed for transaction date {entry.transaction_date}.'
        )
        return redirect('accounting:journal_entry_detail', reference_number=reference_number)
    
    # Validate all accounts are active (Requirement 16.6)
    inactive_accounts = []
    for line in entry.lines.all():
        if not line.account.is_active:
            inactive_accounts.append(f'{line.account.code} - {line.account.name}')
    
    if inactive_accounts:
        messages.error(
            request,
            f'Cannot post journal entry. The following accounts are inactive: '
            f'{", ".join(inactive_accounts)}'
        )
        return redirect('accounting:journal_entry_detail', reference_number=reference_number)
    
    # Post the entry using AccountingService
    accounting_service = AccountingService()
    
    try:
        accounting_service.post_journal_entry(entry, request.user)
        messages.success(
            request,
            f'Journal entry "{entry.reference_number}" posted successfully to general ledger.'
        )
    except ValidationError as e:
        messages.error(request, f'Posting failed: {str(e)}')
    except Exception as e:
        messages.error(request, f'Unexpected error while posting: {str(e)}')
    
    return redirect('accounting:journal_entry_detail', reference_number=reference_number)


@login_required
@staff_required
def journal_entry_reverse(request, reference_number):
    """
    Reverse a posted journal entry.
    Requires a reason for the reversal.
    """
    entry = get_object_or_404(JournalEntry, reference_number=reference_number)
    
    # Validate that entry is posted (Requirement 16.9)
    if entry.status != 'posted':
        messages.error(
            request,
            f'Only posted journal entries can be reversed. '
            f'Current status: {entry.get_status_display()}'
        )
        return redirect('accounting:journal_entry_detail', reference_number=reference_number)
    
    # Validate entry has not already been reversed
    if hasattr(entry, 'reversed_by') and entry.reversed_by:
        messages.error(
            request,
            f'This journal entry has already been reversed by {entry.reversed_by.reference_number}.'
        )
        return redirect('accounting:journal_entry_detail', reference_number=reference_number)
    
    if request.method == 'POST':
        reason = request.POST.get('reason', '').strip()
        
        if not reason:
            messages.error(request, 'A reason for reversal is required.')
            return redirect('accounting:journal_entry_detail', reference_number=reference_number)
        
        # Reverse the entry using AccountingService
        accounting_service = AccountingService()
        
        try:
            reversal_entry = accounting_service.reverse_journal_entry(
                journal_entry=entry,
                user=request.user,
                reversal_date=date.today(),
                reason=reason
            )
            
            messages.success(
                request,
                f'Journal entry "{entry.reference_number}" reversed successfully. '
                f'Reversal entry: {reversal_entry.reference_number}'
            )
            return redirect('accounting:journal_entry_detail', reference_number=reversal_entry.reference_number)
        
        except ValidationError as e:
            messages.error(request, f'Reversal failed: {str(e)}')
        except Exception as e:
            messages.error(request, f'Unexpected error during reversal: {str(e)}')
    
    return redirect('accounting:journal_entry_detail', reference_number=reference_number)


@login_required
@staff_required
def account_search_ajax(request):
    """
    AJAX endpoint for account search/autocomplete.
    Returns JSON list of accounts matching the search query.
    """
    query = request.GET.get('q', '').strip()
    
    if len(query) < 2:
        return JsonResponse({'accounts': []})
    
    # Search accounts by code or name
    accounts = Account.objects.filter(
        Q(code__icontains=query) | Q(name__icontains=query),
        is_active=True
    ).order_by('code')[:20]
    
    results = [
        {
            'id': account.id,
            'code': account.code,
            'name': account.name,
            'type': account.get_account_type_display(),
            'display': f'{account.code} - {account.name}'
        }
        for account in accounts
    ]
    
    return JsonResponse({'accounts': results})



# Export views

@login_required
@login_required
@staff_required
def trial_balance_export(request):
    """
    Export trial balance report to PDF or Excel format.
    """
    from django.http import HttpResponse
    from .services.report_service import ReportService
    from .services.export_service import ExportService
    from datetime import datetime
    
    # Get parameters
    as_of_date = request.GET.get('as_of_date', date.today())
    format_type = request.GET.get('format', 'pdf')  # 'pdf' or 'excel'
    branch_id = request.GET.get('branch')
    
    # Parse date if string
    if isinstance(as_of_date, str):
        as_of_date = datetime.strptime(as_of_date, '%Y-%m-%d').date()
    
    # Get branch if specified
    branch = None
    if branch_id:
        from users.models import Branch
        branch = get_object_or_404(Branch, pk=branch_id)
    
    # Generate report data
    report_service = ReportService()
    report_data = report_service.generate_trial_balance(as_of_date, branch)
    
    # Export to requested format
    export_service = ExportService()
    
    if format_type == 'excel':
        buffer = export_service.export_trial_balance_excel(report_data)
        response = HttpResponse(
            buffer.getvalue(),
            content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
        )
        filename = f"trial_balance_{as_of_date}.xlsx"
    else:  # PDF
        buffer = export_service.export_trial_balance_pdf(report_data, request.user.username)
        response = HttpResponse(buffer.getvalue(), content_type='application/pdf')
        filename = f"trial_balance_{as_of_date}.pdf"
    
    response['Content-Disposition'] = f'attachment; filename="{filename}"'
    return response


@login_required
@staff_required
def income_statement_export(request):
    """
    Export income statement to PDF or Excel format.
    """
    from django.http import HttpResponse
    from .services.report_service import ReportService
    from .services.export_service import ExportService
    from datetime import datetime
    
    # Get parameters
    start_date = request.GET.get('start_date')
    end_date = request.GET.get('end_date', date.today())
    format_type = request.GET.get('format', 'pdf')
    branch_id = request.GET.get('branch')
    
    # Parse dates if strings
    if isinstance(start_date, str):
        start_date = datetime.strptime(start_date, '%Y-%m-%d').date()
    if isinstance(end_date, str):
        end_date = datetime.strptime(end_date, '%Y-%m-%d').date()
    
    # Get branch if specified
    branch = None
    if branch_id:
        from users.models import Branch
        branch = get_object_or_404(Branch, pk=branch_id)
    
    # Generate report data
    report_service = ReportService()
    report_data = report_service.generate_income_statement(start_date, end_date, branch)
    
    # Export to requested format
    export_service = ExportService()
    
    if format_type == 'excel':
        buffer = export_service.export_income_statement_excel(report_data)
        response = HttpResponse(
            buffer.getvalue(),
            content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
        )
        filename = f"income_statement_{start_date}_to_{end_date}.xlsx"
    else:  # PDF
        buffer = export_service.export_income_statement_pdf(report_data, request.user.username)
        response = HttpResponse(buffer.getvalue(), content_type='application/pdf')
        filename = f"income_statement_{start_date}_to_{end_date}.pdf"
    
    response['Content-Disposition'] = f'attachment; filename="{filename}"'
    return response


@login_required
@staff_required
def balance_sheet_export(request):
    """
    Export balance sheet to PDF or Excel format.
    """
    from django.http import HttpResponse
    from .services.report_service import ReportService
    from .services.export_service import ExportService
    from datetime import datetime
    
    # Get parameters
    as_of_date = request.GET.get('as_of_date', date.today())
    format_type = request.GET.get('format', 'pdf')
    branch_id = request.GET.get('branch')
    
    # Parse date if string
    if isinstance(as_of_date, str):
        as_of_date = datetime.strptime(as_of_date, '%Y-%m-%d').date()
    
    # Get branch if specified
    branch = None
    if branch_id:
        from users.models import Branch
        branch = get_object_or_404(Branch, pk=branch_id)
    
    # Generate report data
    report_service = ReportService()
    report_data = report_service.generate_balance_sheet(as_of_date, branch)
    
    # Export to requested format
    export_service = ExportService()
    
    if format_type == 'excel':
        buffer = export_service.export_balance_sheet_excel(report_data)
        response = HttpResponse(
            buffer.getvalue(),
            content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
        )
        filename = f"balance_sheet_{as_of_date}.xlsx"
    else:  # PDF
        buffer = export_service.export_balance_sheet_pdf(report_data, request.user.username)
        response = HttpResponse(buffer.getvalue(), content_type='application/pdf')
        filename = f"balance_sheet_{as_of_date}.pdf"
    
    response['Content-Disposition'] = f'attachment; filename="{filename}"'
    return response


@login_required
@staff_required
def cash_flow_export(request):
    """
    Export cash flow statement to PDF or Excel format.
    """
    from django.http import HttpResponse
    from .services.report_service import ReportService
    from .services.export_service import ExportService
    from datetime import datetime
    
    # Get parameters
    start_date = request.GET.get('start_date')
    end_date = request.GET.get('end_date', date.today())
    format_type = request.GET.get('format', 'pdf')
    branch_id = request.GET.get('branch')
    
    # Parse dates if strings
    if isinstance(start_date, str):
        start_date = datetime.strptime(start_date, '%Y-%m-%d').date()
    if isinstance(end_date, str):
        end_date = datetime.strptime(end_date, '%Y-%m-%d').date()
    
    # Get branch if specified
    branch = None
    if branch_id:
        from users.models import Branch
        branch = get_object_or_404(Branch, pk=branch_id)
    
    # Generate report data
    report_service = ReportService()
    report_data = report_service.generate_cash_flow_statement(start_date, end_date, branch)
    
    # Export to requested format
    export_service = ExportService()
    
    if format_type == 'excel':
        buffer = export_service.export_cash_flow_excel(report_data)
        response = HttpResponse(
            buffer.getvalue(),
            content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
        )
        filename = f"cash_flow_{start_date}_to_{end_date}.xlsx"
    else:  # PDF
        buffer = export_service.export_cash_flow_pdf(report_data, request.user.username)
        response = HttpResponse(buffer.getvalue(), content_type='application/pdf')
        filename = f"cash_flow_{start_date}_to_{end_date}.pdf"
    
    response['Content-Disposition'] = f'attachment; filename="{filename}"'
    return response


@login_required
@staff_required
def account_analysis_export(request):
    """
    Export account analysis report to PDF or Excel format.
    """
    from django.http import HttpResponse
    from .services.report_service import ReportService
    from .services.export_service import ExportService
    from datetime import datetime
    
    # Get parameters
    account_code = request.GET.get('account_code')
    start_date = request.GET.get('start_date')
    end_date = request.GET.get('end_date', date.today())
    format_type = request.GET.get('format', 'pdf')
    
    # Parse dates if strings
    if isinstance(start_date, str):
        start_date = datetime.strptime(start_date, '%Y-%m-%d').date()
    if isinstance(end_date, str):
        end_date = datetime.strptime(end_date, '%Y-%m-%d').date()
    
    # Get account
    account = get_object_or_404(Account, code=account_code)
    
    # Generate report data
    report_service = ReportService()
    report_data = report_service.generate_account_analysis(account, start_date, end_date)
    
    # Export to requested format
    export_service = ExportService()
    
    if format_type == 'excel':
        buffer = export_service.export_account_analysis_excel(report_data)
        response = HttpResponse(
            buffer.getvalue(),
            content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
        )
        filename = f"account_analysis_{account_code}_{start_date}_to_{end_date}.xlsx"
    else:  # PDF
        buffer = export_service.export_account_analysis_pdf(report_data, request.user.username)
        response = HttpResponse(buffer.getvalue(), content_type='application/pdf')
        filename = f"account_analysis_{account_code}_{start_date}_to_{end_date}.pdf"
    
    response['Content-Disposition'] = f'attachment; filename="{filename}"'
    return response



@login_required
@staff_required
def account_analysis_export(request):
    """
    Export account analysis report to PDF or Excel format.
    """
    from django.http import HttpResponse
    from .services.report_service import ReportService
    from .services.export_service import ExportService
    from datetime import datetime
    
    # Get parameters
    account_id = request.GET.get('account_id')
    start_date = request.GET.get('start_date')
    end_date = request.GET.get('end_date', date.today())
    format_type = request.GET.get('format', 'pdf')
    
    # Get account
    account = get_object_or_404(Account, pk=account_id)
    
    # Parse dates if strings
    if isinstance(start_date, str):
        start_date = datetime.strptime(start_date, '%Y-%m-%d').date()
    if isinstance(end_date, str):
        end_date = datetime.strptime(end_date, '%Y-%m-%d').date()
    
    # Generate report data
    report_service = ReportService()
    report_data = report_service.generate_account_analysis(account, start_date, end_date)
    
    # Export to requested format
    export_service = ExportService()
    
    if format_type == 'excel':
        buffer = export_service.export_account_analysis_excel(report_data)
        response = HttpResponse(
            buffer.getvalue(),
            content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
        )
        filename = f"account_analysis_{account.code}_{start_date}_to_{end_date}.xlsx"
    else:  # PDF
        buffer = export_service.export_account_analysis_pdf(report_data, request.user.username)
        response = HttpResponse(buffer.getvalue(), content_type='application/pdf')
        filename = f"account_analysis_{account.code}_{start_date}_to_{end_date}.pdf"
    
    response['Content-Disposition'] = f'attachment; filename="{filename}"'
    return response


# ==================== Fiscal Period Views ====================

@login_required
@staff_required
def fiscal_period_list(request):
    """
    List all fiscal periods sorted by start_date descending.
    Shows period name, type, start/end dates, and status.
    """
    periods = FiscalPeriod.objects.select_related(
        'closed_by'
    ).order_by('-start_date')
    
    # Calculate summary statistics
    total_periods = FiscalPeriod.objects.count()
    open_periods = FiscalPeriod.objects.filter(status='open').count()
    closed_periods = FiscalPeriod.objects.filter(status='closed').count()
    
    # Pagination
    paginator = Paginator(periods, 50)
    page_number = request.GET.get('page', 1)
    periods_page = paginator.get_page(page_number)
    
    context = {
        'periods': periods_page,
        'total_periods': total_periods,
        'open_periods': open_periods,
        'closed_periods': closed_periods,
    }
    
    return render(request, 'accounting/periods/list.html', context)


@login_required
@staff_required
def fiscal_period_detail(request, period_id):
    """
    Display fiscal period details including opening and closing balances.
    """
    period = get_object_or_404(
        FiscalPeriod.objects.select_related('closed_by'),
        pk=period_id
    )
    
    # Get accounting service for balance calculations
    accounting_service = AccountingService()
    
    # Get journal entries in this period
    entries_in_period = JournalEntry.objects.filter(
        transaction_date__gte=period.start_date,
        transaction_date__lte=period.end_date
    ).select_related('branch', 'created_by', 'posted_by').order_by('-transaction_date')
    
    # Count by status
    draft_entries = entries_in_period.filter(status='draft').count()
    posted_entries = entries_in_period.filter(status='posted').count()
    
    # Get a sample of recent entries
    recent_entries = entries_in_period[:10]
    
    context = {
        'period': period,
        'entries_in_period': entries_in_period.count(),
        'draft_entries': draft_entries,
        'posted_entries': posted_entries,
        'recent_entries': recent_entries,
    }
    
    return render(request, 'accounting/periods/detail.html', context)


@login_required
@staff_required
def fiscal_period_create(request):
    """
    Create a new fiscal period with validation.
    """
    if request.method == 'POST':
        form = FiscalPeriodForm(request.POST)
        if form.is_valid():
            period = form.save(commit=False)
            period.status = 'open'
            period.save()
            
            messages.success(
                request,
                f'Fiscal period "{period.name}" created successfully.'
            )
            return redirect('accounting:fiscal_period_detail', period_id=period.pk)
    else:
        form = FiscalPeriodForm()
    
    context = {
        'form': form,
        'title': 'Create Fiscal Period',
        'submit_text': 'Create Period',
    }
    
    return render(request, 'accounting/periods/form.html', context)


@login_required
@staff_required
def fiscal_period_close(request, period_id):
    """
    Close a fiscal period after validation.
    Calculates closing balances and updates period status.
    """
    period = get_object_or_404(FiscalPeriod, pk=period_id)
    
    # Validate period is open
    if period.status != 'open':
        messages.error(
            request,
            f'Cannot close period. Current status: {period.get_status_display()}'
        )
        return redirect('accounting:fiscal_period_detail', period_id=period_id)
    
    if request.method == 'POST':
        try:
            with transaction.atomic():
                # 1. Validate all journal entries are posted (no drafts)
                draft_entries = JournalEntry.objects.filter(
                    transaction_date__gte=period.start_date,
                    transaction_date__lte=period.end_date,
                    status='draft'
                )
                
                if draft_entries.exists():
                    raise ValidationError(
                        f'Cannot close period. There are {draft_entries.count()} '
                        f'draft journal entries that must be posted or deleted first.'
                    )
                
                # 2. Calculate closing balances for all accounts
                accounting_service = AccountingService()
                closing_balances = {}
                
                accounts = Account.objects.filter(is_active=True)
                for account in accounts:
                    balance = accounting_service.calculate_account_balance(
                        account=account,
                        as_of_date=period.end_date,
                        branch=None
                    )
                    if balance != Decimal('0.00'):
                        closing_balances[account.code] = {
                            'account_id': account.id,
                            'account_name': account.name,
                            'balance': str(balance),
                        }
                
                # 3. Store closing balances
                period.closing_balances = closing_balances
                period.status = 'closed'
                period.closed_at = timezone.now()
                period.closed_by = request.user
                period.save()
                
                # 4. Calculate opening balances for next period (if exists)
                next_period = FiscalPeriod.objects.filter(
                    start_date__gt=period.end_date
                ).order_by('start_date').first()
                
                if next_period:
                    # Closing balances of this period become opening balances of next
                    next_period.opening_balances = closing_balances
                    next_period.save()
                
                messages.success(
                    request,
                    f'Fiscal period "{period.name}" closed successfully. '
                    f'Closing balances calculated for {len(closing_balances)} accounts.'
                )
                return redirect('accounting:fiscal_period_detail', period_id=period_id)
        
        except ValidationError as e:
            messages.error(request, str(e))
        except Exception as e:
            messages.error(request, f'Error closing period: {str(e)}')
    
    # Show confirmation page for GET request
    # Count draft entries for warning
    draft_entries_count = JournalEntry.objects.filter(
        transaction_date__gte=period.start_date,
        transaction_date__lte=period.end_date,
        status='draft'
    ).count()
    
    context = {
        'period': period,
        'draft_entries_count': draft_entries_count,
    }
    
    return render(request, 'accounting/periods/close_confirm.html', context)


@login_required
@staff_required
def fiscal_period_reopen(request, period_id):
    """
    Reopen a closed fiscal period.
    Requires 'reopen_fiscal_period' permission.
    """
    period = get_object_or_404(FiscalPeriod, pk=period_id)
    
    # Check permission
    if not request.user.has_perm('accounting.reopen_fiscal_period'):
        messages.error(
            request,
            'You do not have permission to reopen fiscal periods. '
            'Contact your administrator.'
        )
        return redirect('accounting:fiscal_period_detail', period_id=period_id)
    
    # Validate period is closed
    if period.status != 'closed':
        messages.error(
            request,
            f'Cannot reopen period. Current status: {period.get_status_display()}'
        )
        return redirect('accounting:fiscal_period_detail', period_id=period_id)
    
    if request.method == 'POST':
        try:
            with transaction.atomic():
                # Reopen the period
                period.status = 'open'
                period.closed_at = None
                period.closed_by = None
                # Keep closing_balances for historical reference but clear them if needed
                # period.closing_balances = {}
                period.save()
                
                messages.success(
                    request,
                    f'Fiscal period "{period.name}" reopened successfully.'
                )
                return redirect('accounting:fiscal_period_detail', period_id=period_id)
        
        except Exception as e:
            messages.error(request, f'Error reopening period: {str(e)}')
    
    # Show confirmation page for GET request
    context = {
        'period': period,
    }
    
    return render(request, 'accounting/periods/reopen_confirm.html', context)


# ==================== Report Views (Placeholders for Task 13) ====================

@login_required
@staff_required
def reports_dashboard(request):
    """
    Financial dashboard view with key metrics.
    TODO: To be fully implemented in Task 13.1
    """
    messages.info(request, 'Reports dashboard is coming soon.')
    return redirect('accounting:account_list')


@login_required
@staff_required
def trial_balance_report(request):
    """
    Trial balance report view with export options.
    TODO: To be fully implemented in Task 13.2
    """
    messages.info(request, 'Trial balance report view is coming soon.')
    return redirect('accounting:account_list')


@login_required
@staff_required
def income_statement_report(request):
    """
    Income statement report view with export options.
    TODO: To be fully implemented in Task 13.3
    """
    messages.info(request, 'Income statement report view is coming soon.')
    return redirect('accounting:account_list')


@login_required
@staff_required
def balance_sheet_report(request):
    """
    Balance sheet report view with export options.
    TODO: To be fully implemented in Task 13.4
    """
    messages.info(request, 'Balance sheet report view is coming soon.')
    return redirect('accounting:account_list')


@login_required
@staff_required
def cash_flow_report(request):
    """
    Cash flow statement report view with export options.
    TODO: To be fully implemented in Task 13.5
    """
    messages.info(request, 'Cash flow statement report view is coming soon.')
    return redirect('accounting:account_list')


@login_required
@staff_required
def account_analysis_report(request):
    """
    Account analysis report view with export options.
    TODO: To be fully implemented in Task 13.6
    """
    messages.info(request, 'Account analysis report view is coming soon.')
    return redirect('accounting:account_list')


@login_required
@staff_required
def audit_log_report(request):
    """
    Audit log report view with filtering and search.
    
    Displays comprehensive audit trail with filtering by:
    - Date range
    - User
    - Action type
    - Model name or object ID search
    
    Requirements: 14.3, 14.6, 14.7, 14.9
    """
    from .models import AuditLog
    from django.contrib.auth import get_user_model
    
    User = get_user_model()
    
    # Get base queryset
    audit_logs_qs = AuditLog.objects.select_related('user').all()
    
    # Get filter parameters
    start_date = request.GET.get('start_date')
    end_date = request.GET.get('end_date')
    user_id = request.GET.get('user')
    action_type = request.GET.get('action_type')
    search = request.GET.get('search', '').strip()
    
    # Apply date range filter
    if start_date:
        try:
            audit_logs_qs = audit_logs_qs.filter(timestamp__gte=start_date)
        except (ValueError, ValidationError):
            messages.warning(request, 'Invalid start date format.')
    
    if end_date:
        try:
            # Include the entire end date
            from datetime import datetime
            end_datetime = datetime.strptime(end_date, '%Y-%m-%d')
            end_datetime = end_datetime.replace(hour=23, minute=59, second=59)
            audit_logs_qs = audit_logs_qs.filter(timestamp__lte=end_datetime)
        except (ValueError, ValidationError):
            messages.warning(request, 'Invalid end date format.')
    
    # Apply user filter
    if user_id:
        try:
            audit_logs_qs = audit_logs_qs.filter(user_id=user_id)
        except (ValueError, ValidationError):
            messages.warning(request, 'Invalid user selection.')
    
    # Apply action type filter
    if action_type:
        audit_logs_qs = audit_logs_qs.filter(action_type=action_type)
    
    # Apply search filter (model name or object ID)
    if search:
        audit_logs_qs = audit_logs_qs.filter(
            Q(model_name__icontains=search) |
            Q(object_id__icontains=search) |
            Q(description__icontains=search)
        )
    
    # Pagination
    paginator = Paginator(audit_logs_qs, 50)  # 50 logs per page
    page_number = request.GET.get('page')
    audit_logs = paginator.get_page(page_number)
    
    # Get all users for filter dropdown
    users = User.objects.filter(audit_logs__isnull=False).distinct().order_by('first_name', 'last_name')
    
    # Action types for filter dropdown
    action_types = AuditLog.ACTION_TYPES
    
    context = {
        'audit_logs': audit_logs,
        'users': users,
        'action_types': action_types,
        'start_date': start_date,
        'end_date': end_date,
        'selected_user_id': user_id,
        'selected_action_type': action_type,
        'search': search,
    }
    
    return render(request, 'accounting/reports/audit_log.html', context)

