from datetime import datetime
from docx import Document
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
from docx.shared import Pt, RGBColor, Cm
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
import os
from os.path import basename
from loguru import logger as log
import sys


logger = log
logger.remove()
logger.add("logs/application.log", rotation="1 week", enqueue=True)
log.add(
    f"logs/{datetime.now().strftime('%Y-%m-%d')}.log",
    rotation="1 day",
    compression="zip",
)

# logger.add(sys.stderr, format="{time} {level} {message}", level="INFO")
logger.add(sys.stdout)


class SemesterError(Exception):
    """Custom exception for semester-related errors."""

    def __init__(self, message):
        super().__init__(message)
        logger.error(message)

    def __str__(self):
        return f"SemesterError: {self.args[0]}"


class SemesterDocument:
    def __init__(
        self,
        apparats: list[tuple[int, str]],
        semester: str,
        filename,
        config,
        full: bool = False,
    ):
        assert isinstance(apparats, list), SemesterError(
            "Apparats must be a list of tuples"
        )
        assert all(isinstance(apparat, tuple) for apparat in apparats), SemesterError(
            "Apparats must be a list of tuples"
        )
        assert all(isinstance(apparat[0], int) for apparat in apparats), SemesterError(
            "Apparat numbers must be integers"
        )
        assert all(isinstance(apparat[1], str) for apparat in apparats), SemesterError(
            "Apparat names must be strings"
        )
        assert isinstance(semester, str), SemesterError("Semester must be a string")
        assert "." not in filename and isinstance(filename, str), SemesterError(
            "Filename must be a string and not contain an extension"
        )
        self.doc = Document()
        self.apparats = apparats
        self.semester = semester
        self.table_font = "Arial"
        self.header_font = "Times New Roman"
        self.header_font_size = Pt(26)
        self.sub_header_font_size = Pt(18)
        self.table_font_size = Pt(10)
        self.color_red = RGBColor(255, 0, 0)
        self.color_blue = RGBColor(0, 0, 255)
        self.filename = filename
        self.settings = config
        if full:
            logger.info("Full document generation")
            self.make_document()
            logger.info("Document created")
            self.create_pdf()
            logger.info("PDF created")
            self.print_document()
            logger.info("Document printed")
            self.cleanup()
            logger.info("Cleanup done")
    def set_table_border(self, table):
        """
        Adds a full border to the table.

        :param table: Table object to which the border will be applied.
        """
        tbl = table._element
        tbl_pr = tbl.xpath("w:tblPr")[0]
        tbl_borders = OxmlElement("w:tblBorders")

        # Define border styles
        for border_name in ["top", "left", "bottom", "right", "insideH", "insideV"]:
            border = OxmlElement(f"w:{border_name}")
            border.set(qn("w:val"), "single")
            border.set(qn("w:sz"), "4")  # Thickness of the border
            border.set(qn("w:space"), "0")
            border.set(qn("w:color"), "000000")  # Black color
            tbl_borders.append(border)

        tbl_pr.append(tbl_borders)

    def create_sorted_table(self) -> None:
        # Sort the apparats list by the string in the tuple (index 1)
        self.apparats.sort(key=lambda x: x[1])
        # Create a table with rows equal to the length of the apparats list
        table = self.doc.add_table(rows=len(self.apparats), cols=2)
        table.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER

        # Set column widths by directly modifying the cell properties
        widths = [Cm(1.19), Cm(10.39)]
        for col_idx, width in enumerate(widths):
            for cell in table.columns[col_idx].cells:
                cell_width_element = cell._element.xpath(".//w:tcPr")[0]
                tcW = OxmlElement("w:tcW")
                tcW.set(qn("w:w"), str(int(width.cm * 567)))  # Convert cm to twips
                tcW.set(qn("w:type"), "dxa")
                cell_width_element.append(tcW)

        # Adjust row heights
        for row in table.rows:
            trPr = row._tr.get_or_add_trPr()  # Get or add the <w:trPr> element
            trHeight = OxmlElement("w:trHeight")
            trHeight.set(
                qn("w:val"), str(int(Pt(15).pt * 20))
            )  # Convert points to twips
            trHeight.set(qn("w:hRule"), "exact")  # Use "exact" for fixed height
            trPr.append(trHeight)

        # Fill the table with sorted data
        for row_idx, (number, name) in enumerate(self.apparats):
            row = table.rows[row_idx]

            # Set font for the first column (number)
            cell_number_paragraph = row.cells[0].paragraphs[0]
            cell_number_run = cell_number_paragraph.add_run(str(number))
            cell_number_run.font.name = self.table_font
            cell_number_run.font.size = self.table_font_size
            cell_number_run.font.bold = True
            cell_number_run.font.color.rgb = self.color_red
            cell_number_paragraph.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER

            # Set font for the second column (name)
            cell_name_paragraph = row.cells[1].paragraphs[0]
            words = name.split()
            if words:
                # Add the first word in bold
                bold_run = cell_name_paragraph.add_run(words[0])
                bold_run.font.bold = True
                bold_run.font.name = self.table_font
                bold_run.font.size = self.table_font_size

                # Add the rest of the words normally
                if len(words) > 1:
                    normal_run = cell_name_paragraph.add_run(" " + " ".join(words[1:]))
                    normal_run.font.name = self.table_font
                    normal_run.font.size = self.table_font_size
            cell_name_paragraph.alignment = WD_PARAGRAPH_ALIGNMENT.LEFT

        self.set_table_border(table)

    def make_document(self):
        # Create a new Document
        section = self.doc.sections[0]
        section.top_margin = Cm(2.54)  # Default 1 inch (can adjust as needed)
        section.bottom_margin = Cm(1.5)  # Set bottom margin to 1.5 cm
        section.left_margin = Cm(2.54)  # Default 1 inch
        section.right_margin = Cm(2.54)  # Default 1 inch

        # Add the current date
        current_date = datetime.now().strftime("%Y-%m-%d")
        date_paragraph = self.doc.add_paragraph(current_date)
        date_paragraph.alignment = WD_PARAGRAPH_ALIGNMENT.LEFT

        # Add a header
        semester = f"Semesterapparate {self.semester}"
        header = self.doc.add_paragraph(semester)
        header.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        header_run = header.runs[0]
        header_run.font.name = self.header_font
        header_run.font.size = self.header_font_size
        header_run.font.bold = True
        header_run.font.color.rgb = self.color_blue

        sub_header = self.doc.add_paragraph("(Alphabetisch)")
        sub_header.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        sub_header_run = sub_header.runs[0]
        sub_header_run.font.name = self.header_font
        sub_header_run.font.size = self.sub_header_font_size
        sub_header_run.font.color.rgb = self.color_red

        self.doc.add_paragraph("")

        self.create_sorted_table()

    def save_document(self, name):
        # Save the document
        self.doc.save(name)
        print(f"Document saved as {name}")

    def print_document(self):
        # send document to printer as attachment of email
        import smtplib
        from email.mime.multipart import MIMEMultipart
        from email.mime.application import MIMEApplication
        from email.mime.text import MIMEText

        config = self.settings
        smtp = config.mail.smtp_server
        port = config.mail.port
        sender_email = config.mail.sender
        password = config.mail.password
        receiver = config.mail.printer_mail
        message = MIMEMultipart()
        message["From"] = sender_email
        message["To"] = receiver
        message["cc"] = config.mail.sender
        message["Subject"] = "."
        mail_body = "."
        message.attach(MIMEText(mail_body, "html"))
        with open(self.filename + ".pdf", "rb") as fil:
            part = MIMEApplication(fil.read(), Name=basename(self.filename + "pdf"))
        # After the file is closed
        part["Content-Disposition"] = 'attachment; filename="%s"' % basename(
            self.filename + ".pdf"
        )
        message.attach(part)
        mail = message.as_string()
        with smtplib.SMTP_SSL(smtp, port) as server:
            server.connect(smtp, port)
            server.login(config.mail.user_name, password)
            server.sendmail(sender_email, receiver, mail)
            server.quit()
            print("Mail sent")

    def create_pdf(self):
        # Save the document
        import comtypes.client

        word = comtypes.client.CreateObject("Word.Application")
        self.save_document(self.filename + ".docx")
        docpath = os.path.abspath(self.filename + ".docx")
        doc = word.Documents.Open(docpath)
        curdir = os.getcwd()
        doc.SaveAs(f"{curdir}/{self.filename}.pdf", FileFormat=17)
        doc.Close()
        word.Quit()
        logger.debug("PDF saved")

    def cleanup(self):
        os.remove(f"{self.filename}.docx")
        os.remove(f"{self.filename}.pdf")


if __name__ == "__main__":
    pass
    # apparat = [(i, f"Item {i}") for i in range(405, 438)]
    # doc = SemesterDocument(
    #     apparat,
    #     "WiSe 24/25",
    #     "semap",
    # )
    # doc.make_document()
    # doc.create_pdf()
    # doc.print_document()


# def printers():
#     printers = win32print.EnumPrinters(
#         win32print.PRINTER_ENUM_LOCAL | win32print.PRINTER_ENUM_CONNECTIONS
#     )
#     for i, printer in enumerate(printers):
#         print(f"{i}: {printer[2]}")

# list printers
