from datetime import date, timedelta, datetime
from dateutil.relativedelta import relativedelta
from decimal import Decimal, ROUND_DOWN, ROUND_HALF_UP
from reportlab.lib.pagesizes import letter
from reportlab.lib import colors
from reportlab.pdfgen import canvas
from reportlab.platypus import Table, TableStyle
import locale

# Imposta la locale appropriata (ad esempio per l'italiano)
try:
    locale.setlocale(locale.LC_ALL, 'it_IT.UTF-8')  # Prova questa prima
except locale.Error:
    try:
        locale.setlocale(locale.LC_ALL, 'Italian_Italy')  # Prova questa se fallisce
    except locale.Error:
        locale.setlocale(locale.LC_ALL, 'Italian')  # Ultima possibilità
class InstallmentPlan:
    HOLIDAYS = [
        (1, 1),   # Capodanno
        (1, 6),   # Epifania
        (4, 25),  # Festa della Liberazione
        (5, 1),   # Festa del Lavoro
        (6, 2),   # Festa della Repubblica
        (8, 15),  # Ferragosto
        (11, 1),  # Ognissanti
        (12, 8),  # Immacolata Concezione
        (12, 25), # Natale
        (12, 26)  # Santo Stefano
    ]

    def __init__(self, total_debt, communication_date, processing_date, is_electronic, num_installments):
        self.total_debt = Decimal(str(total_debt)).quantize(Decimal('0.01'))
        self.communication_date = communication_date
        self.processing_date = processing_date
        self.is_electronic = is_electronic
        self.num_installments = min(num_installments, 20)
        self.installments = []
        self.interest_start_date = self._calculate_interest_start_date()
        self._generate_payment_schedule()

    def _is_holiday(self, d):
        return d.weekday() >= 5 or (d.month, d.day) in self.HOLIDAYS or d == self._calculate_easter(d.year)

    def _next_working_day(self, d):
        while self._is_holiday(d):
            d += timedelta(days=1)
        return d

    def _calculate_easter(self, year):
        a = year % 19
        b = year // 100
        c = year % 100
        d = b // 4
        e = b % 4
        f = (b + 8) // 25
        g = (b - f + 1) // 3
        h = (19 * a + b - d - g + 15) % 30
        i = c // 4
        k = c % 4
        l = (32 + 2 * e + 2 * i - h - k) % 7
        m = (a + 11 * h + 22 * l) // 451
        month = (h + l - 7 * m + 114) // 31
        day = ((h + l - 7 * m + 114) % 31) + 1
        return date(year, month, day)

    def _calculate_interest_start_date(self):
        start_date = self.processing_date + relativedelta(months=2)
        start_date = start_date.replace(day=1)
        return self._next_working_day(start_date)

    def _calculate_first_due_date(self):
        base_days = 30
        suspension_start = date(self.communication_date.year, 8, 1)
        suspension_end = date(self.communication_date.year, 9, 5)

        if self.communication_date < suspension_start:
            temp_due_date = self.communication_date + timedelta(days=base_days)
            if temp_due_date < suspension_start:
                raw_due_date = temp_due_date
            else:
                days_before_suspension = (suspension_start - self.communication_date).days
                remaining_days = base_days - days_before_suspension
                raw_due_date = suspension_end + timedelta(days=remaining_days)
        elif self.communication_date < suspension_end:
            raw_due_date = suspension_end + timedelta(days=base_days)
        else:
            raw_due_date = self.communication_date + timedelta(days=base_days)

        adjusted_due_date = self._next_working_day(raw_due_date)
        return raw_due_date, adjusted_due_date

    def _generate_payment_schedule(self):
        if self.num_installments == 0:
            return

        rata_base = (self.total_debt / self.num_installments).quantize(Decimal('0.01'), ROUND_DOWN)
        totale_ripartito = rata_base * self.num_installments
        differenza_totale = self.total_debt - totale_ripartito

        raw_first, adjusted_first = self._calculate_first_due_date()
        self.installments.append({
            'raw_due_date': raw_first,
            'due_date': adjusted_first,
            'principal': rata_base + differenza_totale,
            'interest': Decimal('0')
        })

        raw_date = raw_first
        for _ in range(self.num_installments - 1):
            raw_date = (raw_date + relativedelta(months=3)).replace(day=1) + relativedelta(months=1) - timedelta(days=1)
            adjusted_date = self._next_working_day(raw_date)
            self.installments.append({
                'raw_due_date': raw_date,
                'due_date': adjusted_date,
                'principal': rata_base,
                'interest': None
            })

    def calculate_interest(self, installment_num):
        if installment_num == 0 or installment_num >= len(self.installments):
            return Decimal('0')

        rata = self.installments[installment_num]
        capitale = rata['principal']
        interest_start = self.interest_start_date
        due_date = rata['raw_due_date']
        giorni = (due_date - interest_start).days + 1
        return (capitale * Decimal('0.035') * Decimal(giorni) / Decimal('365')).quantize(Decimal('0.01'), ROUND_HALF_UP)

    def get_payment_schedule(self):
        schedule = []
        total_days = 0
        total_interest = Decimal('0')
        for i, rata in enumerate(self.installments):
            interest = self.calculate_interest(i) if i > 0 else Decimal('0')
            total_interest += interest
            total_days += (rata['raw_due_date'] - self.interest_start_date).days + 1
            schedule.append({
                'raw_due_date': rata['raw_due_date'],
                'due_date': rata['due_date'],
                'principal': rata['principal'],
                'interest': interest
            })
        daily_interest = total_interest / total_days if total_days > 0 else Decimal('0')
        return schedule, total_days, daily_interest

    def get_annual_interest_summary(self):
        summary = {}
        schedule, _, _ = self.get_payment_schedule()
        for rata in schedule:
            year = rata['raw_due_date'].year
            if year not in summary:
                summary[year] = {
                    'interest': Decimal('0'),
                    'start_date': rata['raw_due_date'],
                    'end_date': rata['raw_due_date'],
                    'days_count': 0  # Aggiunta per il conteggio dei giorni
                }
            summary[year]['interest'] += rata['interest']
            summary[year]['end_date'] = rata['raw_due_date']  # Aggiorna sempre la data di fine

        return summary

class PDFGenerator:
    def __init__(self, filename):
        self.filename = filename

    def format_number(self, value):
            """Formatta un numero decimale in stringa senza zeri non necessari."""
            # Arrotonda il numero a due cifre decimali per la formattazione
            rounded_value = value.quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
            
            # Formatta il numero in una stringa
            formatted_value = f"{rounded_value:,.2f}".replace(",", " ").replace(".", ",")  # Sostituisce i punti con virgole
            return formatted_value
        

    def arrotonda_personalizzato(self, valore):
        if isinstance(valore, (int, float)):  # Applica l'arrotondamento solo se il valore è numerico
            intero_centi = int(valore * 100)
            successiva = int((valore * 1000) % 10)

            if successiva > 5:
                intero_centi += 1

            return intero_centi / 100
        return valore
        

    def generate_pdf(self, plan):
        c = canvas.Canvas(self.filename, pagesize=letter)
        c.setFont("Helvetica", 10)

        schedule, total_days, daily_interest = plan.get_payment_schedule()
        annual_summary = plan.get_annual_interest_summary()

        # Imposta i margini
        left_margin = 50
        y_position_start_table = 130
        
        

        data = [['Rata', 'Raw Due Date', 'Adjusted Due Date', 'Capitale', 'Interessi', 'Totale']]
        total_interest = Decimal('0')

        for i, rata in enumerate(schedule):
            totale = rata['principal'] + (rata['interest'] if rata['interest'] is not None else 0)
            total_interest += rata['interest']
            data.append([
                i + 1,
                rata['raw_due_date'].strftime('%d/%m/%Y'),
                rata['due_date'].strftime('%d/%m/%Y'), 
                f"€{self.format_number(rata['principal'])}",
                f"€{self.format_number(rata['interest'] if rata['interest'] is not None else 0)}", 
                f"€{self.format_number(totale)}"
            ])

        # Creare la tabella delle rate
        table = Table(data, colWidths=[50, 90, 100, 80, 80, 80])
        table.setStyle(TableStyle([ 
            ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
            ('BACKGROUND', (0, 0), (-1, 0), colors.black),
            ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
            ('GRID', (0, 0), (-1, -1), 0.5, colors.black),
            ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
            ('FONTNAME', (0, 1), (-1, -1), 'Helvetica'),
        ]))

        # Controllo dell'altezza della tabella
        table_height = table.wrap(0, 0)[1]  # Ottieni l'altezza della tabella
        
        # Disegnare la tabella delle rate
        y_position = y_position_start_table
        if y_position - table_height < 50:  # Se non c'è spazio
            c.showPage()  # Aggiungi una nuova pagina
            y_position = 750  # Ripristina la posizione in alto

        table.drawOn(c, left_margin, y_position - table_height)
        
        # Differenza tra la prima e l'ultima scadenza
        first_due_date = schedule[0]['raw_due_date']
        last_due_date = schedule[-1]['raw_due_date']

        # Calcolare il numero totale di giorni tra la prima e l'ultima scadenza
        total_days = (last_due_date - first_due_date).days + 1
        daily_interest = Decimal(total_interest / total_days).quantize(Decimal('0.00000000'))

        # Stampa informazioni aggiuntive sotto la tabella delle rate
        y_position -= table_height + 20  # Sposta indietro il cursore
        c.drawString(left_margin, y_position, f"Giorni totali: {total_days}")
        y_position -= 20
        c.drawString(left_margin, y_position, f"Interessi totali: €{self.format_number(total_interest)}")
        y_position -= 20
        c.drawString(left_margin, y_position, f"Quota giornaliera di interessi: {daily_interest}")

        c.drawString(left_margin, y_position - 20, "Riepilogo annuale degli interessi:")
        y_position -= 20  # Spazio extra per il riepilogo annuale


DOBBIAMO ELIMINARE La prima pagina bianca del pdf e prima della tabella dobbiamo stampare il codice atto