import hashlib
import hmac
import io
import logging

import qrcode
from reportlab.lib import colors
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet
from reportlab.lib.units import cm, mm
from reportlab.platypus import (
    Image,
    Paragraph,
    SimpleDocTemplate,
    Spacer,
    Table,
    TableStyle,
)

_logger = logging.getLogger(__name__)


class PdfGenerator:

    @staticmethod
    def generate_exam_report(env, attempt_id):
        """Generate a PDF exam report with skill-by-skill scores and a QR verification code."""
        attempt = env['encoach.student.attempt'].sudo().browse(attempt_id)
        if not attempt.exists():
            raise ValueError(f"Attempt {attempt_id} not found")

        buf = io.BytesIO()
        doc = SimpleDocTemplate(buf, pagesize=A4,
                                leftMargin=2 * cm, rightMargin=2 * cm,
                                topMargin=2 * cm, bottomMargin=2 * cm)
        styles = getSampleStyleSheet()
        title_style = ParagraphStyle('ReportTitle', parent=styles['Title'],
                                     fontSize=20, spaceAfter=12)
        heading_style = ParagraphStyle('SectionHeading', parent=styles['Heading2'],
                                       fontSize=14, spaceAfter=8)

        elements = []

        elements.append(Paragraph("EnCoach Exam Report", title_style))
        elements.append(Spacer(1, 6 * mm))

        student_name = getattr(attempt, 'student_id', False) and attempt.student_id.name or 'N/A'
        exam_title = getattr(attempt, 'exam_id', False) and attempt.exam_id.name or 'N/A'
        attempt_date = getattr(attempt, 'create_date', '') or ''

        info_data = [
            ['Student', student_name],
            ['Exam', exam_title],
            ['Date', str(attempt_date)[:19]],
        ]
        info_table = Table(info_data, colWidths=[4 * cm, 12 * cm])
        info_table.setStyle(TableStyle([
            ('FONTNAME', (0, 0), (0, -1), 'Helvetica-Bold'),
            ('FONTNAME', (1, 0), (1, -1), 'Helvetica'),
            ('FONTSIZE', (0, 0), (-1, -1), 11),
            ('BOTTOMPADDING', (0, 0), (-1, -1), 6),
        ]))
        elements.append(info_table)
        elements.append(Spacer(1, 8 * mm))

        elements.append(Paragraph("Skill Scores", heading_style))

        scores = env['encoach.score'].sudo().search([
            ('attempt_id', '=', attempt_id),
        ])
        score_header = ['Skill', 'Band Score']
        score_rows = [score_header]
        for score in scores:
            skill_name = getattr(score, 'skill_name', '') or getattr(score, 'name', 'N/A')
            band = getattr(score, 'band_score', '') or getattr(score, 'score', 'N/A')
            score_rows.append([str(skill_name), str(band)])

        if len(score_rows) > 1:
            score_table = Table(score_rows, colWidths=[8 * cm, 8 * cm])
            score_table.setStyle(TableStyle([
                ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#2C3E50')),
                ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
                ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
                ('FONTSIZE', (0, 0), (-1, -1), 10),
                ('GRID', (0, 0), (-1, -1), 0.5, colors.grey),
                ('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.whitesmoke, colors.white]),
                ('BOTTOMPADDING', (0, 0), (-1, -1), 6),
                ('TOPPADDING', (0, 0), (-1, -1), 6),
            ]))
            elements.append(score_table)
        elements.append(Spacer(1, 6 * mm))

        overall_band = getattr(attempt, 'overall_band', 'N/A')
        cefr_level = getattr(attempt, 'cefr_level', 'N/A')
        elements.append(Paragraph(f"Overall Band: <b>{overall_band}</b>", styles['Normal']))
        elements.append(Paragraph(f"CEFR Level: <b>{cefr_level}</b>", styles['Normal']))
        elements.append(Spacer(1, 4 * mm))

        feedback = getattr(attempt, 'feedback_summary', '') or ''
        if feedback:
            elements.append(Paragraph("Feedback", heading_style))
            elements.append(Paragraph(str(feedback), styles['Normal']))
            elements.append(Spacer(1, 6 * mm))

        verification_hash = PdfGenerator._generate_verification_hash(env, attempt_id)
        base_url = env['ir.config_parameter'].sudo().get_param('web.base.url', '')
        verify_url = f"{base_url}/api/verify/{verification_hash}"
        qr_img = PdfGenerator._build_qr_code(verify_url)

        qr_buf = io.BytesIO()
        qr_img.save(qr_buf, format='PNG')
        qr_buf.seek(0)

        elements.append(Paragraph("Verification", heading_style))
        elements.append(Paragraph("Scan to verify this report:", styles['Normal']))
        elements.append(Spacer(1, 2 * mm))
        elements.append(Image(qr_buf, width=4 * cm, height=4 * cm))

        doc.build(elements)
        return buf.getvalue()

    @staticmethod
    def _generate_verification_hash(env, attempt_id):
        """Create HMAC-SHA256 hash for attempt verification."""
        secret = env['ir.config_parameter'].sudo().get_param(
            'encoach.verification_secret', 'default-secret-change-me'
        )
        attempt = env['encoach.student.attempt'].sudo().browse(attempt_id)
        student_name = getattr(attempt, 'student_id', False) and attempt.student_id.name or ''
        exam_name = getattr(attempt, 'exam_id', False) and attempt.exam_id.name or ''
        message = f"{attempt_id}:{student_name}:{exam_name}"
        return hmac.new(
            secret.encode('utf-8'),
            message.encode('utf-8'),
            hashlib.sha256,
        ).hexdigest()

    @staticmethod
    def _build_qr_code(url):
        """Generate a QR code PIL Image from a URL."""
        qr = qrcode.QRCode(
            version=1,
            error_correction=qrcode.constants.ERROR_CORRECT_M,
            box_size=10,
            border=4,
        )
        qr.add_data(url)
        qr.make(fit=True)
        return qr.make_image(fill_color="black", back_color="white").get_image()
