"""
Financial Utilities
Report generation, data export, calculations
"""
from django.db.models import Sum, Avg, Count
from django.utils import timezone
from datetime import timedelta
from decimal import Decimal
import csv
from io import StringIO
from io import BytesIO
from django.conf import settings
from ai_features.models import AIConfiguration
import json
try:
    import requests
except Exception:
    requests = None
from urllib import request as urllib_request
from urllib.error import URLError, HTTPError
from urllib.parse import urljoin

from .models import Revenue, Expense, Transaction, FinancialReport
from .models import FinancialAccount


class FinancialReportGenerator:
    """
    Generate financial reports
    """
    
    @staticmethod
    def generate_income_statement(start_date, end_date):
        """
        Generate Income Statement (P&L)
        """
        # Revenue
        revenues = Revenue.objects.filter(
            revenue_date__gte=start_date,
            revenue_date__lte=end_date
        )
        
        total_revenue = revenues.aggregate(
            total=Sum('net_amount')
        )['total'] or Decimal('0')
        
        # Expenses
        expenses = Expense.objects.filter(
            expense_date__gte=start_date,
            expense_date__lte=end_date,
            payment_status='PAID'
        )
        
        total_expenses = expenses.aggregate(
            total=Sum('amount')
        )['total'] or Decimal('0')
        
        # Calculate
        gross_profit = total_revenue
        operating_expenses = total_expenses
        net_profit = gross_profit - operating_expenses
        
        report_data = {
            'period_start': start_date,
            'period_end': end_date,
            'revenue': {
                'total': float(total_revenue),
                'by_type': list(revenues.values('revenue_type').annotate(
                    total=Sum('net_amount')
                ))
            },
            'expenses': {
                'total': float(total_expenses),
                'by_category': list(expenses.values('category').annotate(
                    total=Sum('amount')
                ))
            },
            'gross_profit': float(gross_profit),
            'net_profit': float(net_profit),
            'profit_margin': float((net_profit / total_revenue * 100) if total_revenue > 0 else 0)
        }
        
        # Save report
        report = FinancialReport.objects.create(
            report_type='INCOME_STATEMENT',
            report_name=f"Income Statement - {start_date} to {end_date}",
            period_start=start_date,
            period_end=end_date,
            total_revenue=total_revenue,
            total_expenses=total_expenses,
            net_profit=net_profit,
            report_data=report_data
        )
        
        return report


class BankAPIClient:
    def __init__(self):
        config = None
        try:
            config = AIConfiguration.get_settings()
        except Exception:
            config = None
        base_from_config = getattr(config, 'bank_api_base_url', '') if config else ''
        token_from_config = getattr(config, 'bank_api_token', '') if config else ''
        self.base_url = (base_from_config or getattr(settings, 'BANK_API_BASE_URL', '')).rstrip('/')
        self.token = token_from_config or getattr(settings, 'BANK_API_TOKEN', '')
        self.timeout = 15
    def _headers(self):
        h = {'Content-Type': 'application/json'}
        if self.token:
            h['Authorization'] = f'Bearer {self.token}'
        return h
    def _post(self, path, payload):
        if not self.base_url:
            return {'success': False, 'message': 'Bank API not configured'}
        url = urljoin(self.base_url + '/', path.lstrip('/'))
        data = json.dumps(payload).encode('utf-8')
        if requests:
            try:
                r = requests.post(url, headers=self._headers(), data=data, timeout=self.timeout)
                try:
                    body = r.json()
                except Exception:
                    body = {'message': r.text}
                return {'status_code': r.status_code, 'body': body}
            except Exception as e:
                return {'success': False, 'message': str(e)}
        req = urllib_request.Request(url, data=data, headers=self._headers(), method='POST')
        try:
            with urllib_request.urlopen(req, timeout=self.timeout) as resp:
                b = resp.read().decode('utf-8')
                try:
                    body = json.loads(b)
                except Exception:
                    body = {'message': b}
                return {'status_code': resp.getcode(), 'body': body}
        except HTTPError as e:
            try:
                b = e.read().decode('utf-8')
                msg = b
            except Exception:
                msg = str(e)
            return {'success': False, 'message': msg, 'status_code': e.code}
        except URLError as e:
            return {'success': False, 'message': str(e)}
    def initiate_transfer(self, account_number, ifsc, name, amount, reference):
        payload = {
            'beneficiary': {
                'account_number': account_number,
                'ifsc': ifsc,
                'name': name,
            },
            'amount': float(amount),
            'currency': 'INR',
            'reference': reference,
        }
        return self._post('/transfers/initiate', payload)
    def verify_account(self, account_number, ifsc, name):
        payload = {
            'account_number': account_number,
            'ifsc': ifsc,
            'name': name,
        }
        return self._post('/accounts/verify', payload)

    @staticmethod
    def generate_balance_sheet(as_of_date):
        """
        Generate Balance Sheet
        """
        assets = FinancialAccount.objects.filter(account_type='ASSET')
        liabilities = FinancialAccount.objects.filter(account_type='LIABILITY')
        equity = FinancialAccount.objects.filter(account_type='EQUITY')
        
        total_assets = assets.aggregate(total=Sum('current_balance'))['total'] or Decimal('0')
        total_liabilities = liabilities.aggregate(total=Sum('current_balance'))['total'] or Decimal('0')
        total_equity = equity.aggregate(total=Sum('current_balance'))['total'] or Decimal('0')
        
        report_data = {
            'as_of_date': as_of_date,
            'assets': {
                'total': float(total_assets),
                'accounts': list(assets.values('code', 'name', 'current_balance'))
            },
            'liabilities': {
                'total': float(total_liabilities),
                'accounts': list(liabilities.values('code', 'name', 'current_balance'))
            },
            'equity': {
                'total': float(total_equity),
                'accounts': list(equity.values('code', 'name', 'current_balance'))
            }
        }
        
        report = FinancialReport.objects.create(
            report_type='BALANCE_SHEET',
            report_name=f"Balance Sheet - {as_of_date}",
            period_start=as_of_date,
            period_end=as_of_date,
            total_revenue=Decimal('0'),
            total_expenses=Decimal('0'),
            net_profit=Decimal('0'),
            report_data=report_data
        )
        
        return report

    @staticmethod
    def generate_balance_sheet_pdf(report):
        """
        Generate Balance Sheet PDF
        """
        try:
            from reportlab.lib.pagesizes import A4
            from reportlab.pdfgen import canvas
            from reportlab.lib.units import cm
        except Exception:
            return None
        
        buffer = BytesIO()
        c = canvas.Canvas(buffer, pagesize=A4)
        width, height = A4
        
        y = height - 2 * cm
        c.setFont("Helvetica-Bold", 16)
        c.drawString(2 * cm, y, report.report_name)
        y -= 1 * cm
        c.setFont("Helvetica", 12)
        c.drawString(2 * cm, y, f"As of: {report.period_end}")
        
        y -= 1.2 * cm
        c.setFont("Helvetica-Bold", 14)
        c.drawString(2 * cm, y, "Assets")
        y -= 0.8 * cm
        c.setFont("Helvetica", 12)
        assets_total = float(report.report_data.get('assets', {}).get('total', 0) or 0)
        assets_total = round(assets_total)
        c.drawString(2 * cm, y, f"Total Assets: ₹ {assets_total:,.0f}.00")
        y -= 0.6 * cm
        
        y -= 0.6 * cm
        c.setFont("Helvetica-Bold", 14)
        c.drawString(2 * cm, y, "Liabilities")
        y -= 0.8 * cm
        c.setFont("Helvetica", 12)
        liabilities_total = float(report.report_data.get('liabilities', {}).get('total', 0) or 0)
        liabilities_total = round(liabilities_total)
        c.drawString(2 * cm, y, f"Total Liabilities: ₹ {liabilities_total:,.0f}.00")
        y -= 0.6 * cm
        
        y -= 0.6 * cm
        c.setFont("Helvetica-Bold", 14)
        c.drawString(2 * cm, y, "Equity")
        y -= 0.8 * cm
        c.setFont("Helvetica", 12)
        equity_total = float(report.report_data.get('equity', {}).get('total', 0) or 0)
        equity_total = round(equity_total)
        c.drawString(2 * cm, y, f"Total Equity: ₹ {equity_total:,.0f}.00")
        
        c.showPage()
        c.save()
        pdf_bytes = buffer.getvalue()
        buffer.close()
        return pdf_bytes

    @staticmethod
    def generate_cash_flow(start_date, end_date):
        cash = FinancialAccount.objects.filter(code='1000').first()
        inflow = Transaction.objects.filter(
            transaction_date__date__gte=start_date,
            transaction_date__date__lte=end_date,
            debit_account__code='1000'
        ).aggregate(total=Sum('amount'))['total'] or Decimal('0')
        outflow = Transaction.objects.filter(
            transaction_date__date__gte=start_date,
            transaction_date__date__lte=end_date,
            credit_account__code='1000'
        ).aggregate(total=Sum('amount'))['total'] or Decimal('0')
        net = inflow - outflow
        report_data = {
            'period_start': start_date,
            'period_end': end_date,
            'inflow': float(inflow),
            'outflow': float(outflow),
            'net_cash_flow': float(net),
            'cash_account': cash.code if cash else '1000'
        }
        report = FinancialReport.objects.create(
            report_type='CASH_FLOW',
            report_name=f"Cash Flow - {start_date} to {end_date}",
            period_start=start_date,
            period_end=end_date,
            total_revenue=Decimal('0'),
            total_expenses=Decimal('0'),
            net_profit=Decimal('0'),
            report_data=report_data
        )
        return report

    @staticmethod
    def generate_cash_flow_pdf(report):
        try:
            from reportlab.lib.pagesizes import A4
            from reportlab.pdfgen import canvas
            from reportlab.lib.units import cm
        except Exception:
            return None
        buffer = BytesIO()
        c = canvas.Canvas(buffer, pagesize=A4)
        width, height = A4
        y = height - 2 * cm
        c.setFont("Helvetica-Bold", 16)
        c.drawString(2 * cm, y, report.report_name)
        y -= 1 * cm
        c.setFont("Helvetica", 12)
        c.drawString(2 * cm, y, f"Period: {report.period_start} to {report.period_end}")
        y -= 1.2 * cm
        c.setFont("Helvetica-Bold", 14)
        c.drawString(2 * cm, y, "Summary")
        y -= 0.8 * cm
        c.setFont("Helvetica", 12)
        inflow = float(report.report_data.get('inflow', 0) or 0)
        inflow = round(inflow)
        c.drawString(2 * cm, y, f"Inflow: ₹ {inflow:,.0f}.00")
        y -= 0.6 * cm
        outflow = float(report.report_data.get('outflow', 0) or 0)
        outflow = round(outflow)
        c.drawString(2 * cm, y, f"Outflow: ₹ {outflow:,.0f}.00")
        y -= 0.6 * cm
        net_cf = float(report.report_data.get('net_cash_flow', 0) or 0)
        net_cf = round(net_cf)
        c.drawString(2 * cm, y, f"Net Cash Flow: ₹ {net_cf:,.0f}.00")
        c.showPage()
        c.save()
        pdf_bytes = buffer.getvalue()
        buffer.close()
        return pdf_bytes
    @staticmethod
    def export_to_csv(queryset, filename):
        """
        Export data to CSV
        """
        output = StringIO()
        writer = csv.writer(output)
        
        # Write headers
        if queryset.model.__name__ == 'Revenue':
            writer.writerow(['Date', 'Type', 'Description', 'Gross', 'Discount', 'Tax', 'Net'])
            for item in queryset:
                writer.writerow([
                    item.revenue_date,
                    item.get_revenue_type_display(),
                    item.description,
                    item.gross_amount,
                    item.discount,
                    item.tax_amount,
                    item.net_amount
                ])
        elif queryset.model.__name__ == 'Expense':
            writer.writerow(['Date', 'Category', 'Description', 'Amount', 'Vendor', 'Status'])
            for item in queryset:
                writer.writerow([
                    item.expense_date,
                    item.get_category_display(),
                    item.description,
                    item.amount,
                    item.vendor_name,
                    item.payment_status
                ])
        
        return output.getvalue()

    @staticmethod
    def generate_income_statement_pdf(report):
        """
        Generate a simple PDF for the given FinancialReport.
        Returns bytes or None if PDF library is unavailable.
        """
        try:
            from reportlab.lib.pagesizes import A4
            from reportlab.pdfgen import canvas
            from reportlab.lib.units import cm
        except Exception:
            return None
        
        buffer = BytesIO()
        c = canvas.Canvas(buffer, pagesize=A4)
        width, height = A4
        
        y = height - 2 * cm
        c.setFont("Helvetica-Bold", 16)
        c.drawString(2 * cm, y, report.report_name)
        y -= 1 * cm
        c.setFont("Helvetica", 12)
        c.drawString(2 * cm, y, f"Period: {report.period_start} to {report.period_end}")
        y -= 0.8 * cm
        c.drawString(2 * cm, y, f"Generated: {report.generated_at.strftime('%Y-%m-%d %H:%M')}")
        
        y -= 1.2 * cm
        c.setFont("Helvetica-Bold", 14)
        c.drawString(2 * cm, y, "Summary")
        y -= 0.8 * cm
        c.setFont("Helvetica", 12)
        total_rev = float(report.total_revenue or 0)
        total_rev = round(total_rev)
        c.drawString(2 * cm, y, f"Total Revenue: ₹ {total_rev:,.0f}.00")
        y -= 0.6 * cm
        total_exp = float(report.total_expenses or 0)
        total_exp = round(total_exp)
        c.drawString(2 * cm, y, f"Total Expenses: ₹ {total_exp:,.0f}.00")
        y -= 0.6 * cm
        net_profit = float(report.net_profit or 0)
        net_profit = round(net_profit)
        c.drawString(2 * cm, y, f"Net Profit: ₹ {net_profit:,.0f}.00")
        
        if report.report_data:
            y -= 1.0 * cm
            c.setFont("Helvetica-Bold", 14)
            c.drawString(2 * cm, y, "Details")
            y -= 0.8 * cm
            c.setFont("Helvetica", 12)
            for key in ['total_revenue', 'total_expenses', 'gross_profit', 'net_profit', 'profit_margin']:
                if key in report.report_data:
                    val = report.report_data[key]
                    label = key.replace('_', ' ').title()
                    c.drawString(2 * cm, y, f"{label}: {val}")
                    y -= 0.6 * cm
                    if y < 2 * cm:
                        c.showPage()
                        y = height - 2 * cm
                        c.setFont("Helvetica", 12)
        
        c.showPage()
        c.save()
        pdf_bytes = buffer.getvalue()
        buffer.close()
        return pdf_bytes


class FinancialCalculator:
    """
    Financial calculations and metrics
    """
    
    @staticmethod
    def calculate_profit_margin(revenue, expenses):
        """Calculate profit margin percentage"""
        if revenue > 0:
            profit = revenue - expenses
            return (profit / revenue) * 100
        return 0
    
    @staticmethod
    def calculate_roa(net_income, total_assets):
        """Return on Assets"""
        if total_assets > 0:
            return (net_income / total_assets) * 100
        return 0
    
    @staticmethod
    def calculate_current_ratio(current_assets, current_liabilities):
        """Current Ratio (Liquidity)"""
        if current_liabilities > 0:
            return current_assets / current_liabilities
        return 0
    
    @staticmethod
    def calculate_burn_rate(period_days=30):
        """
        Cash burn rate (for startups)
        How much cash spent per day
        """
        end_date = timezone.now().date()
        start_date = end_date - timedelta(days=period_days)
        
        total_expenses = Expense.objects.filter(
            expense_date__gte=start_date,
            expense_date__lte=end_date,
            payment_status='PAID'
        ).aggregate(total=Sum('amount'))['total'] or Decimal('0')
        
        daily_burn = total_expenses / period_days
        return float(daily_burn)
    
    @staticmethod
    def calculate_runway(current_cash):
        """
        Cash runway - how many days until running out of money
        """
        daily_burn = FinancialCalculator.calculate_burn_rate()
        if daily_burn > 0:
            return int(current_cash / daily_burn)
        return 999  # Infinite


class TaxCalculator:
    """
    Tax calculations
    """
    
    @staticmethod
    def calculate_sales_tax(amount, tax_rate):
        """Calculate sales tax"""
        return amount * (tax_rate / 100)
    
    @staticmethod
    def calculate_vat(net_amount, vat_rate):
        """Calculate VAT"""
        return net_amount * (vat_rate / 100)
    
    @staticmethod
    def quarterly_tax_summary(year, quarter):
        """
        Get quarterly tax summary
        quarter: 1-4
        """
        from .models import TaxRecord
        
        # Determine quarter dates
        quarter_starts = {
            1: (1, 1),
            2: (4, 1),
            3: (7, 1),
            4: (10, 1),
        }
        
        quarter_ends = {
            1: (3, 31),
            2: (6, 30),
            3: (9, 30),
            4: (12, 31),
        }
        
        from datetime import date
        start_month, start_day = quarter_starts[quarter]
        end_month, end_day = quarter_ends[quarter]
        
        start_date = date(year, start_month, start_day)
        end_date = date(year, end_month, end_day)
        
        taxes = TaxRecord.objects.filter(
            period_start__gte=start_date,
            period_end__lte=end_date
        )
        
        return {
            'total': taxes.aggregate(Sum('tax_amount'))['tax_amount__sum'] or 0,
            'paid': taxes.filter(is_paid=True).aggregate(Sum('tax_amount'))['tax_amount__sum'] or 0,
            'unpaid': taxes.filter(is_paid=False).aggregate(Sum('tax_amount'))['tax_amount__sum'] or 0,
            'by_type': taxes.values('tax_type').annotate(total=Sum('tax_amount'))
        }
