import os
import sys
import textwrap
from typing import Tuple

from django.utils.translation import gettext_lazy as _
from PIL import Image, ImageDraw, ImageFont, UnidentifiedImageError

from poizonstore.settings import BASE_DIR


def create_preview(source_image: str, size=None, price_rub=None, title_lines=None):
    # Create image
    preview_width, preview_height = 800, 600
    hor_padding = 30
    vert_padding = 30
    left_block_width = 270  # minimal width

    canvas_img = Image.new('RGBA', (preview_width, preview_height), color='white')
    draw = ImageDraw.Draw(canvas_img)

    def get_font(font_size):
        font_path = os.path.join(BASE_DIR, 'static', 'preview_image_font.ttf')
        return ImageFont.truetype(font_path, size=font_size)

    def draw_text(xy: Tuple[float, float], text: str, font: ImageFont, multiline_width: int = None, **kwargs):
        x, y = xy

        block_width = 0
        block_height = 0

        if multiline_width is None:
            text = [text]
        else:
            text = textwrap.wrap(text, width=multiline_width)

        for line in text:
            _, _, line_width, line_height = font.getbbox(line)

            draw.text((x, y), line, font=font, **kwargs)
            y += line_height

            block_width = max(block_width, line_width)
            block_height += line_height

        return block_width, block_height

    def resize_with_ar(image, size):
        max_w, max_h = size

        if image.height > image.width:
            factor = max_h / image.height
        else:
            factor = max_w / image.width

        return image.resize((int(image.width * factor), int(image.height * factor)))

    if not os.path.isfile(source_image):
        return None

    # Draw top text
    top_font = get_font(20)
    text = 'Заказ в Poizon Store'
    block_size = draw_text((hor_padding, vert_padding), text, font=top_font, fill='black')
    left_block_width = max(left_block_width, block_size[0] + hor_padding)

    # Draw title
    title_x, title_y = hor_padding, vert_padding + 30
    if title_lines:
        title_font = get_font(40)

        for title_line in title_lines:
            width, height = draw_text((title_x, title_y), title_line, font=title_font, multiline_width=12, fill='black')
            title_y += height

            left_block_width = max(left_block_width, width + title_x)

    # Draw size
    if size:
        size_text = str(size)
        size_font = get_font(20)
        size_padding = 7

        _, _, line_width, line_height = size_font.getbbox(size_text)

        rect_x, rect_y = hor_padding, title_y + 30
        rect_width, rect_height = (line_width + size_padding * 2), (line_height + size_padding * 2)
        rect_width, rect_height = max(40, rect_width), max(40, rect_height)


        # size text is slightly inside a rect
        size_x, size_y = (rect_x + rect_width/2), (rect_y + rect_height/2)

        draw.rectangle((rect_x, rect_y, rect_x + rect_width, rect_y + rect_height), fill='black', width=2)
        draw_text((size_x, size_y), size_text, font=size_font, fill='white', anchor='mm')
        left_block_width = max(left_block_width, rect_width)

    # Draw price
    if price_rub:
        # Format price as '1 000 000' string (by 3 digits with spaces)
        price_text = format(int(price_rub), ',').replace(',', ' ')
        price_text = f"{price_text} ₽"
        price_font = get_font(50)
        price_x, price_y = hor_padding, preview_height - vert_padding

        block_size = draw_text((price_x, price_y), price_text, anchor='ls', font=price_font, fill='black')
        left_block_width = max(left_block_width, block_size[0] + price_x)

    # Draw goods image
    img2_box_w = preview_width - left_block_width - hor_padding * 2
    img2_box_y = preview_height - vert_padding * 2

    try:
        with Image.open(source_image).convert("RGBA") as img2:
            img2 = resize_with_ar(img2, (img2_box_w, img2_box_y))

            img2_x = left_block_width + int(img2_box_w / 2) - int(img2.width / 2) + hor_padding
            img2_y = vert_padding + int(img2_box_y / 2) - int(img2.height / 2)

            canvas_img.paste(img2, (img2_x, img2_y), mask=img2)

            # Debug only
            # img2_box = (img2_x, img2_y, img2_x + img2.width, img2_y + img2.height)
            # draw.rectangle(img2_box, outline='red', width=2)
            #
            # left_block_box = (hor_padding, vert_padding, left_block_width, preview_height - 2*vert_padding)
            # draw.rectangle(left_block_box, outline='red', width=2)

            return canvas_img.convert('RGB')
    except UnidentifiedImageError:
        return None


def concat_not_null_values(*values, separator=' '):
    return separator.join([v for v in values if v is not None])


def get_primary_key_related_model(model_class, **kwargs):
    """
    Info: https://stackoverflow.com/a/43742949
    Nested serializers are a mess.
    This lets us accept ids when saving / updating instead of nested objects.
    Representation would be into an object (depending on model_class).
    """
    class PrimaryKeyNestedMixin(model_class):
        default_error_messages = {
            'does_not_exist': _('Invalid pk "{pk_value}" - object does not exist.'),
            'incorrect_type': _('Incorrect type. Expected pk value, received {data_type}.'),
        }

        def to_internal_value(self, data):
            try:
                return model_class.Meta.model.objects.get(pk=data)
            except model_class.Meta.model.DoesNotExist:
                self.fail('does_not_exist', pk_value=data)
            except (TypeError, ValueError):
                self.fail('incorrect_type', data_type=type(data).__name__)

        def to_representation(self, data):
            return model_class.to_representation(self, data)

    return PrimaryKeyNestedMixin(**kwargs)


def is_migration_running():
    return 'makemigrations' in sys.argv or 'migrate' in sys.argv
