"""Module for constructing and interacting with PDF reports for Pylinac."""
from __future__ import annotations

import io
from datetime import datetime
from pathlib import Path
from typing import Sequence

from PIL import Image
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import cm
from reportlab.lib.utils import ImageReader
from reportlab.pdfgen.canvas import Canvas

from .. import __version__
from .io import get_url


def get_logo() -> Path:
    """Get the Pylinac logo. First try the embedded version, then try the internet.
    When pylinac is embedded in RadMachine as a package non-py files aren't correctly uncompressed.
    """
    logo_file = Path(__file__).parent.parent / "files" / "Pylinac Full cropped.png"
    if logo_file.exists():
        return logo_file
    else:
        filepath = get_url(
            url=r"https://storage.googleapis.com/pylinac_demo_files/Pylinac_Full_cropped.png",
            progress_bar=False,
        )
        return Path(filepath)


class PylinacCanvas:
    def __init__(
        self,
        filename: str,
        page_title: str,
        font: str = "Helvetica",
        metadata: dict | None = None,
        metadata_location: tuple = (2, 25.5),
        logo: Path | str | None = None,
    ):
        self.canvas = Canvas(filename, pagesize=A4)
        self._font = font
        self._title = page_title
        self._metadata = metadata
        self._logo = logo or get_logo()
        self._metadata_location = metadata_location
        self._generate_pylinac_template_theme()
        self._add_metadata()

    def add_new_page(self) -> None:
        self.canvas.showPage()
        self._generate_pylinac_template_theme()
        self._add_metadata()

    def _add_metadata(self) -> None:
        if self._metadata is None:
            return
        else:
            text = ["Metadata:"]
            for key, value in self._metadata.items():
                text.append(f"{key}: {value}")
            self.add_text(text=text, location=self._metadata_location)

    def _generate_pylinac_template_theme(self) -> None:
        # draw logo and header separation line
        self.canvas.drawImage(
            self._logo,
            1 * cm,
            26.5 * cm,
            width=5 * cm,
            height=3 * cm,
            mask="auto",
            preserveAspectRatio=True,
        )
        self.canvas.line(1 * cm, 26.5 * cm, 20 * cm, 26.5 * cm)
        # draw title
        self.add_text(text=self._title, location=(7, 28), font_size=24)
        # draw "generated by pylinac" tag
        date = datetime.now().strftime("%B %d, %Y, %H:%M")
        self.add_text(
            f"Generated with Pylinac v{__version__} on {date}",
            location=(0.5, 0.5),
            font_size=8,
        )

    def add_text(
        self,
        text: str | list[str],
        location: (float, float),
        font_size: int = 10,
        font: str | None = None,
    ) -> None:
        """Generic text drawing function.

        Parameters
        ----------
        location : Sequence of two numbers
            The first item is the distance from left edge in cm
            The second item is the distance from bottom edge in cm.
        text : str, list of strings
            Text data; if str, prints single line.
            If list of strings, each list item is printed on its own line.
        font_size : int
            Text font size.
        """
        font = font or self._font
        textobj = self.canvas.beginText()
        textobj.setTextOrigin(location[0] * cm, location[1] * cm)
        textobj.setFont(font, int(font_size))
        if isinstance(text, str):
            textobj.textLine(text)
        elif isinstance(text, list):
            for line in text:
                textobj.textLine(line)
        self.canvas.drawText(textobj)

    def add_image(
        self,
        image_data: io.BytesIO,
        location: Sequence,
        dimensions: Sequence,
        preserve_aspect_ratio: bool = True,
    ) -> None:
        image_data.seek(0)
        image = ImageReader(Image.open(image_data))
        self.canvas.drawImage(
            image,
            location[0] * cm,
            location[1] * cm,
            width=dimensions[0] * cm,
            height=dimensions[1] * cm,
            preserveAspectRatio=preserve_aspect_ratio,
        )

    def finish(self) -> None:
        self.canvas.showPage()
        self.canvas.save()
