from django.db.models import Count, Sum
from django.db.models.functions import TruncMonth
from django.utils import timezone
from rest_framework import permissions, viewsets
from rest_framework.response import Response
from rest_framework.views import APIView

from apps.debts.models import Debt
from apps.debts.services import sync_company_debts
from apps.expenses.models import Expense
from apps.invoicing.models import Invoice
from apps.payments.models import Payment

from .books import PERIODS, compute_books
from .insights import compute_business_insights
from .models import GeneratedReport
from .serializers import GeneratedReportSerializer
from .services import compute_cash_flow_forecast, compute_financial_health, month_start as _month_start


class GeneratedReportViewSet(viewsets.ReadOnlyModelViewSet):
    serializer_class = GeneratedReportSerializer
    permission_classes = [permissions.IsAuthenticated]
    filterset_fields = ['report_type']

    def get_queryset(self):
        return GeneratedReport.objects.filter(company=self.request.user.company)


class DashboardSummaryView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        company = request.user.company
        today = timezone.now().date()
        month_start = today.replace(day=1)

        if company:
            sync_company_debts(company)

        invoices = Invoice.objects.filter(company=company)
        payments_total = invoices.aggregate(total=Sum('amount_paid'))['total'] or 0

        data = {
            'total_sales': invoices.aggregate(total=Sum('total'))['total'] or 0,
            'total_payments_received': payments_total,
            'pending_payments': invoices.filter(
                status__in=[Invoice.Status.SENT, Invoice.Status.VIEWED, Invoice.Status.PARTIALLY_PAID]
            ).count(),
            'outstanding_debt': Debt.objects.filter(company=company, is_resolved=False).aggregate(
                total=Sum('outstanding_amount')
            )['total'] or 0,
            'overdue_invoices': invoices.filter(status=Invoice.Status.OVERDUE).count(),
            'upcoming_due_payments': invoices.filter(
                due_date__gte=today, due_date__lte=today.replace(day=28)
            ).exclude(status=Invoice.Status.PAID).count(),
            'total_expenses': Expense.objects.filter(company=company).aggregate(total=Sum('amount'))['total'] or 0,
            'monthly_revenue': invoices.filter(issue_date__gte=month_start).aggregate(
                total=Sum('total')
            )['total'] or 0,
        }
        return Response(data)


class DashboardChartsView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        company = request.user.company
        today = timezone.now().date()
        range_start = _month_start(today, 5)

        invoiced_rows = (
            Invoice.objects.filter(company=company, issue_date__gte=range_start)
            .exclude(status=Invoice.Status.CANCELLED)
            .annotate(month=TruncMonth('issue_date'))
            .values('month')
            .annotate(total=Sum('total'))
        )
        invoiced_by_month = {row['month'].strftime('%Y-%m'): row['total'] for row in invoiced_rows}

        collected_rows = (
            Payment.objects.filter(invoice__company=company, payment_date__gte=range_start)
            .annotate(month=TruncMonth('payment_date'))
            .values('month')
            .annotate(total=Sum('amount'))
        )
        collected_by_month = {row['month'].strftime('%Y-%m'): row['total'] for row in collected_rows}

        monthly_trend = []
        for i in range(5, -1, -1):
            month_date = _month_start(today, i)
            key = month_date.strftime('%Y-%m')
            monthly_trend.append({
                'month': month_date.strftime('%b %Y'),
                'invoiced': invoiced_by_month.get(key, 0),
                'collected': collected_by_month.get(key, 0),
            })

        expense_rows = list(
            Expense.objects.filter(company=company)
            .values('category__name')
            .annotate(total=Sum('amount'))
            .order_by('-total')
        )
        top_expenses = expense_rows[:6]
        other_total = sum(row['total'] for row in expense_rows[6:])
        expense_breakdown = [
            {'category': row['category__name'], 'total': row['total']} for row in top_expenses
        ]
        if other_total > 0:
            expense_breakdown.append({'category': 'Other', 'total': other_total})

        status_rows = (
            Invoice.objects.filter(company=company)
            .values('status')
            .annotate(count=Count('id'))
        )
        status_counts = {row['status']: row['count'] for row in status_rows}
        invoice_status = [
            {'status': choice_value, 'count': status_counts.get(choice_value, 0)}
            for choice_value, _ in Invoice.Status.choices
        ]

        if company:
            sync_company_debts(company)
        debt_rows = (
            Debt.objects.filter(company=company, is_resolved=False)
            .values('aging_bucket')
            .annotate(total=Sum('outstanding_amount'))
        )
        debt_totals = {row['aging_bucket']: row['total'] for row in debt_rows}
        debt_aging = [
            {'bucket': choice_value, 'total': debt_totals.get(choice_value, 0)}
            for choice_value, _ in Debt.AgingBucket.choices
        ]

        return Response({
            'monthly_trend': monthly_trend,
            'expense_breakdown': expense_breakdown,
            'invoice_status': invoice_status,
            'debt_aging': debt_aging,
        })


class FinancialHealthView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        company = request.user.company
        if not company:
            return Response({'score': None, 'band': 'insufficient_data', 'components': [], 'insights': []})

        sync_company_debts(company)
        return Response(compute_financial_health(company))


class CashFlowForecastView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        company = request.user.company
        if not company:
            return Response({'starting_balance': 0, 'weekly_outflow_estimate': 0, 'weeks': [], 'shortfall_week': None})

        return Response(compute_cash_flow_forecast(company))


class BusinessInsightsView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        company = request.user.company
        if not company:
            return Response({'insights': []})

        return Response({'insights': compute_business_insights(company)})


class BooksView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        company = request.user.company
        period = request.query_params.get('period', 'this_month')
        if period not in PERIODS:
            period = 'this_month'
        if not company:
            return Response({'period': None, 'income_statement': None, 'tax': None, 'ledger': []})

        return Response(compute_books(company, period))
