"""
GST Invoice PDF Generator
Generates GST-compliant invoices for Indian businesses
"""
import logging
from io import BytesIO
from datetime import datetime
from decimal import Decimal
from typing import Optional, Dict, Any
import json

from reportlab.lib import colors
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import mm, inch
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_RIGHT, TA_LEFT
from reportlab.platypus import (
    SimpleDocTemplate, Table, TableStyle, Paragraph,
    Spacer, Image, HRFlowable
)
from reportlab.pdfgen import canvas

from app.models.billing import Invoice
from app.models.client import Client
from app.config import settings

logger = logging.getLogger(__name__)


# Company Details (configured in settings)
COMPANY_DETAILS = {
    "name": "AI360 Solutions Pvt. Ltd.",
    "address_line1": "123, Tech Park",
    "address_line2": "Whitefield, Bangalore - 560066",
    "state": "Karnataka",
    "state_code": "29",
    "gstin": "29AAACA1234A1ZZ",  # Sample GSTIN
    "pan": "AAACA1234A",
    "cin": "U72200KA2024PTC123456",
    "email": "billing@ai360.com",
    "phone": "+91 80 1234 5678",
    "website": "www.ai360.com",
    "bank_name": "HDFC Bank",
    "bank_account": "50100123456789",
    "bank_ifsc": "HDFC0001234",
    "bank_branch": "Whitefield Branch, Bangalore",
}


class GSTInvoicePDFGenerator:
    """Generate GST-compliant invoice PDFs"""

    def __init__(self):
        self.styles = getSampleStyleSheet()
        self._setup_custom_styles()

    def _setup_custom_styles(self):
        """Setup custom paragraph styles"""
        self.styles.add(ParagraphStyle(
            name='InvoiceTitle',
            parent=self.styles['Heading1'],
            fontSize=18,
            textColor=colors.HexColor('#1a365d'),
            spaceAfter=6,
            alignment=TA_CENTER
        ))

        self.styles.add(ParagraphStyle(
            name='SectionHeader',
            parent=self.styles['Heading2'],
            fontSize=11,
            textColor=colors.HexColor('#2d3748'),
            spaceBefore=10,
            spaceAfter=5,
            fontName='Helvetica-Bold'
        ))

        self.styles.add(ParagraphStyle(
            name='TableHeader',
            parent=self.styles['Normal'],
            fontSize=9,
            textColor=colors.white,
            fontName='Helvetica-Bold',
            alignment=TA_CENTER
        ))

        self.styles.add(ParagraphStyle(
            name='SmallText',
            parent=self.styles['Normal'],
            fontSize=8,
            textColor=colors.HexColor('#4a5568')
        ))

        self.styles.add(ParagraphStyle(
            name='CompanyName',
            parent=self.styles['Normal'],
            fontSize=14,
            fontName='Helvetica-Bold',
            textColor=colors.HexColor('#1a365d')
        ))

    def generate_invoice_pdf(
        self,
        invoice: Invoice,
        client: Client,
        output_path: Optional[str] = None
    ) -> bytes:
        """
        Generate GST-compliant invoice PDF

        Args:
            invoice: Invoice model instance
            client: Client model instance
            output_path: Optional file path to save PDF

        Returns:
            PDF content as bytes
        """
        buffer = BytesIO()

        # Create document
        doc = SimpleDocTemplate(
            buffer,
            pagesize=A4,
            rightMargin=15*mm,
            leftMargin=15*mm,
            topMargin=15*mm,
            bottomMargin=20*mm
        )

        # Build elements
        elements = []

        # Header with company info
        elements.extend(self._build_header(invoice))
        elements.append(Spacer(1, 5*mm))

        # Invoice details and billing info
        elements.extend(self._build_invoice_details(invoice, client))
        elements.append(Spacer(1, 8*mm))

        # Line items table
        elements.extend(self._build_line_items_table(invoice))
        elements.append(Spacer(1, 5*mm))

        # Tax summary
        elements.extend(self._build_tax_summary(invoice))
        elements.append(Spacer(1, 8*mm))

        # Amount in words
        elements.extend(self._build_amount_in_words(invoice))
        elements.append(Spacer(1, 8*mm))

        # Bank details
        elements.extend(self._build_bank_details())
        elements.append(Spacer(1, 8*mm))

        # Terms and signature
        elements.extend(self._build_footer(invoice))

        # Build PDF
        doc.build(elements)

        pdf_content = buffer.getvalue()
        buffer.close()

        # Save to file if path provided
        if output_path:
            with open(output_path, 'wb') as f:
                f.write(pdf_content)

        return pdf_content

    def _build_header(self, invoice: Invoice) -> list:
        """Build invoice header with company logo and details"""
        elements = []

        # Company name and Tax Invoice title
        header_data = [
            [
                Paragraph(COMPANY_DETAILS["name"], self.styles['CompanyName']),
                Paragraph("<b>TAX INVOICE</b>", ParagraphStyle(
                    'TaxInvoice',
                    fontSize=16,
                    fontName='Helvetica-Bold',
                    textColor=colors.HexColor('#e53e3e'),
                    alignment=TA_RIGHT
                ))
            ],
            [
                Paragraph(f"{COMPANY_DETAILS['address_line1']}<br/>{COMPANY_DETAILS['address_line2']}", self.styles['SmallText']),
                Paragraph(f"Invoice No: <b>{invoice.invoice_number}</b>", ParagraphStyle(
                    'InvoiceNo',
                    fontSize=10,
                    alignment=TA_RIGHT
                ))
            ],
            [
                Paragraph(f"GSTIN: {COMPANY_DETAILS['gstin']}", self.styles['SmallText']),
                Paragraph(f"Date: <b>{invoice.issue_date.strftime('%d-%b-%Y')}</b>", ParagraphStyle(
                    'InvoiceDate',
                    fontSize=10,
                    alignment=TA_RIGHT
                ))
            ],
            [
                Paragraph(f"PAN: {COMPANY_DETAILS['pan']} | CIN: {COMPANY_DETAILS['cin']}", self.styles['SmallText']),
                ""
            ]
        ]

        header_table = Table(header_data, colWidths=[100*mm, 80*mm])
        header_table.setStyle(TableStyle([
            ('VALIGN', (0, 0), (-1, -1), 'TOP'),
            ('ALIGN', (1, 0), (1, -1), 'RIGHT'),
        ]))

        elements.append(header_table)

        # Horizontal line
        elements.append(HRFlowable(
            width="100%",
            thickness=1,
            color=colors.HexColor('#cbd5e0'),
            spaceBefore=3*mm,
            spaceAfter=3*mm
        ))

        return elements

    def _build_invoice_details(self, invoice: Invoice, client: Client) -> list:
        """Build billing and shipping details section"""
        elements = []

        # Parse GST notes for tax breakdown
        gst_notes = {}
        if invoice.notes:
            try:
                gst_notes = json.loads(invoice.notes) if isinstance(invoice.notes, str) else invoice.notes
            except:
                pass

        # Determine if same state (CGST+SGST) or different state (IGST)
        is_same_state = True  # Assume same state for now

        # Two-column layout for billing details
        billing_data = [
            [
                Paragraph("<b>Bill To:</b>", self.styles['SectionHeader']),
                Paragraph("<b>Invoice Details:</b>", self.styles['SectionHeader'])
            ],
            [
                Paragraph(f"<b>{client.company_name}</b>", self.styles['Normal']),
                f"Billing Period: {invoice.billing_period_start.strftime('%d-%b-%Y')} to {invoice.billing_period_end.strftime('%d-%b-%Y')}"
            ],
            [
                Paragraph(f"{client.address or 'Address not provided'}", self.styles['SmallText']),
                f"Due Date: {invoice.due_date.strftime('%d-%b-%Y')}"
            ],
            [
                Paragraph(f"GSTIN: {client.gstin or 'Not Registered'}", self.styles['SmallText']),
                f"Place of Supply: {COMPANY_DETAILS['state']} ({COMPANY_DETAILS['state_code']})"
            ],
            [
                Paragraph(f"State: {client.state or 'N/A'} | State Code: {client.state_code or 'N/A'}", self.styles['SmallText']),
                f"Supply Type: {'Intra-State' if is_same_state else 'Inter-State'}"
            ]
        ]

        details_table = Table(billing_data, colWidths=[100*mm, 80*mm])
        details_table.setStyle(TableStyle([
            ('VALIGN', (0, 0), (-1, -1), 'TOP'),
            ('FONTSIZE', (0, 0), (-1, -1), 9),
        ]))

        elements.append(details_table)
        return elements

    def _build_line_items_table(self, invoice: Invoice) -> list:
        """Build line items table with GST details"""
        elements = []

        # Table header
        header = [
            'S.No',
            'Description',
            'HSN/SAC',
            'Qty',
            'Rate (₹)',
            'Amount (₹)'
        ]

        # Build table data
        table_data = [header]
        line_items = invoice.line_items or []

        for idx, item in enumerate(line_items, 1):
            row = [
                str(idx),
                Paragraph(item.get('description', ''), self.styles['Normal']),
                item.get('hsn_sac', '998314'),
                str(item.get('quantity', 1)),
                f"{float(item.get('unit_price', 0)):,.2f}",
                f"{float(item.get('amount', 0)):,.2f}"
            ]
            table_data.append(row)

        # Column widths
        col_widths = [10*mm, 75*mm, 20*mm, 15*mm, 30*mm, 30*mm]

        items_table = Table(table_data, colWidths=col_widths, repeatRows=1)
        items_table.setStyle(TableStyle([
            # Header styling
            ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#2d3748')),
            ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
            ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
            ('FONTSIZE', (0, 0), (-1, 0), 9),
            ('ALIGN', (0, 0), (-1, 0), 'CENTER'),

            # Body styling
            ('FONTSIZE', (0, 1), (-1, -1), 9),
            ('ALIGN', (0, 1), (0, -1), 'CENTER'),  # S.No
            ('ALIGN', (2, 1), (2, -1), 'CENTER'),  # HSN
            ('ALIGN', (3, 1), (3, -1), 'CENTER'),  # Qty
            ('ALIGN', (4, 1), (4, -1), 'RIGHT'),   # Rate
            ('ALIGN', (5, 1), (5, -1), 'RIGHT'),   # Amount

            # Borders
            ('GRID', (0, 0), (-1, -1), 0.5, colors.HexColor('#cbd5e0')),
            ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
            ('TOPPADDING', (0, 0), (-1, -1), 4),
            ('BOTTOMPADDING', (0, 0), (-1, -1), 4),

            # Alternating row colors
            ('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, colors.HexColor('#f7fafc')]),
        ]))

        elements.append(items_table)
        return elements

    def _build_tax_summary(self, invoice: Invoice) -> list:
        """Build tax summary section"""
        elements = []

        # Parse GST notes
        gst_notes = {}
        if invoice.notes:
            try:
                gst_notes = json.loads(invoice.notes) if isinstance(invoice.notes, str) else invoice.notes
            except:
                pass

        cgst_rate = gst_notes.get('cgst_rate', 9)
        cgst_amount = gst_notes.get('cgst_amount', float(invoice.tax_amount) / 2)
        sgst_rate = gst_notes.get('sgst_rate', 9)
        sgst_amount = gst_notes.get('sgst_amount', float(invoice.tax_amount) / 2)

        # Summary table
        summary_data = [
            ['', '', '', 'Subtotal:', f"₹ {float(invoice.subtotal):,.2f}"],
        ]

        if invoice.discount_amount and float(invoice.discount_amount) > 0:
            summary_data.append(
                ['', '', '', 'Discount:', f"- ₹ {float(invoice.discount_amount):,.2f}"]
            )

        summary_data.extend([
            ['', '', '', f'CGST @ {cgst_rate}%:', f"₹ {cgst_amount:,.2f}"],
            ['', '', '', f'SGST @ {sgst_rate}%:', f"₹ {sgst_amount:,.2f}"],
        ])

        # Total
        summary_data.append(
            ['', '', '', Paragraph('<b>Total:</b>', self.styles['Normal']),
             Paragraph(f"<b>₹ {float(invoice.total_amount):,.2f}</b>", ParagraphStyle(
                 'TotalAmount',
                 fontSize=12,
                 fontName='Helvetica-Bold',
                 alignment=TA_RIGHT
             ))]
        )

        col_widths = [10*mm, 75*mm, 20*mm, 35*mm, 40*mm]
        summary_table = Table(summary_data, colWidths=col_widths)
        summary_table.setStyle(TableStyle([
            ('ALIGN', (3, 0), (3, -1), 'RIGHT'),
            ('ALIGN', (4, 0), (4, -1), 'RIGHT'),
            ('FONTSIZE', (0, 0), (-1, -1), 9),
            ('TOPPADDING', (0, 0), (-1, -1), 3),
            ('BOTTOMPADDING', (0, 0), (-1, -1), 3),
            ('LINEABOVE', (3, -1), (-1, -1), 1, colors.HexColor('#2d3748')),
            ('BACKGROUND', (3, -1), (-1, -1), colors.HexColor('#edf2f7')),
        ]))

        elements.append(summary_table)
        return elements

    def _build_amount_in_words(self, invoice: Invoice) -> list:
        """Build amount in words section"""
        elements = []

        amount_in_words = self._number_to_words(float(invoice.total_amount))

        words_data = [
            [Paragraph(f"<b>Amount in Words:</b> {amount_in_words} Only", self.styles['Normal'])]
        ]

        words_table = Table(words_data, colWidths=[180*mm])
        words_table.setStyle(TableStyle([
            ('BACKGROUND', (0, 0), (-1, -1), colors.HexColor('#f7fafc')),
            ('BOX', (0, 0), (-1, -1), 0.5, colors.HexColor('#cbd5e0')),
            ('TOPPADDING', (0, 0), (-1, -1), 5),
            ('BOTTOMPADDING', (0, 0), (-1, -1), 5),
            ('LEFTPADDING', (0, 0), (-1, -1), 5),
        ]))

        elements.append(words_table)
        return elements

    def _build_bank_details(self) -> list:
        """Build bank details section"""
        elements = []

        elements.append(Paragraph("<b>Bank Details for Payment:</b>", self.styles['SectionHeader']))

        bank_data = [
            ['Bank Name:', COMPANY_DETAILS['bank_name']],
            ['Account No:', COMPANY_DETAILS['bank_account']],
            ['IFSC Code:', COMPANY_DETAILS['bank_ifsc']],
            ['Branch:', COMPANY_DETAILS['bank_branch']],
        ]

        bank_table = Table(bank_data, colWidths=[30*mm, 80*mm])
        bank_table.setStyle(TableStyle([
            ('FONTSIZE', (0, 0), (-1, -1), 9),
            ('FONTNAME', (0, 0), (0, -1), 'Helvetica-Bold'),
            ('TOPPADDING', (0, 0), (-1, -1), 2),
            ('BOTTOMPADDING', (0, 0), (-1, -1), 2),
        ]))

        elements.append(bank_table)
        return elements

    def _build_footer(self, invoice: Invoice) -> list:
        """Build terms and signature section"""
        elements = []

        footer_data = [
            [
                Paragraph("<b>Terms & Conditions:</b><br/>"
                         "1. Payment is due within 15 days of invoice date.<br/>"
                         "2. Late payment may attract interest @ 18% p.a.<br/>"
                         "3. All disputes subject to Bangalore jurisdiction.", self.styles['SmallText']),
                Paragraph(f"<b>For {COMPANY_DETAILS['name']}</b><br/><br/><br/><br/>"
                         "Authorized Signatory", ParagraphStyle(
                             'Signature',
                             fontSize=9,
                             alignment=TA_CENTER
                         ))
            ]
        ]

        footer_table = Table(footer_data, colWidths=[100*mm, 80*mm])
        footer_table.setStyle(TableStyle([
            ('VALIGN', (0, 0), (-1, -1), 'TOP'),
            ('ALIGN', (1, 0), (1, -1), 'CENTER'),
        ]))

        elements.append(footer_table)

        # Computer generated notice
        elements.append(Spacer(1, 5*mm))
        elements.append(Paragraph(
            "<i>This is a computer-generated invoice and does not require a signature.</i>",
            ParagraphStyle('Notice', fontSize=8, alignment=TA_CENTER, textColor=colors.gray)
        ))

        return elements

    def _number_to_words(self, num: float) -> str:
        """Convert number to words (Indian numbering system)"""
        if num == 0:
            return "Zero Rupees"

        ones = ['', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine',
                'Ten', 'Eleven', 'Twelve', 'Thirteen', 'Fourteen', 'Fifteen', 'Sixteen',
                'Seventeen', 'Eighteen', 'Nineteen']
        tens = ['', '', 'Twenty', 'Thirty', 'Forty', 'Fifty', 'Sixty', 'Seventy', 'Eighty', 'Ninety']

        def _two_digits(n):
            if n < 20:
                return ones[n]
            else:
                return tens[n // 10] + (' ' + ones[n % 10] if n % 10 else '')

        def _three_digits(n):
            if n < 100:
                return _two_digits(n)
            else:
                return ones[n // 100] + ' Hundred' + (' ' + _two_digits(n % 100) if n % 100 else '')

        # Split into rupees and paise
        rupees = int(num)
        paise = int(round((num - rupees) * 100))

        result = []

        # Crores
        if rupees >= 10000000:
            result.append(_two_digits(rupees // 10000000) + ' Crore')
            rupees %= 10000000

        # Lakhs
        if rupees >= 100000:
            result.append(_two_digits(rupees // 100000) + ' Lakh')
            rupees %= 100000

        # Thousands
        if rupees >= 1000:
            result.append(_two_digits(rupees // 1000) + ' Thousand')
            rupees %= 1000

        # Hundreds
        if rupees >= 100:
            result.append(_three_digits(rupees))
        elif rupees > 0:
            result.append(_two_digits(rupees))

        rupees_str = ' '.join(result) + ' Rupees' if result else 'Zero Rupees'

        if paise > 0:
            rupees_str += ' and ' + _two_digits(paise) + ' Paise'

        return rupees_str


# Singleton instance
invoice_pdf_generator = GSTInvoicePDFGenerator()


def generate_gst_invoice_pdf(invoice: Invoice, client: Client, output_path: Optional[str] = None) -> bytes:
    """
    Generate GST invoice PDF

    Args:
        invoice: Invoice model
        client: Client model
        output_path: Optional output file path

    Returns:
        PDF content as bytes
    """
    return invoice_pdf_generator.generate_invoice_pdf(invoice, client, output_path)
