"""
This command will replace roadrunner_bs4 pages with roadrunner_bs5 pages.
"""

import json
from string import Template

from django.apps import apps
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.core.management import call_command
from django.core.management.base import BaseCommand
from django.db import connection, transaction

from wagtail.models import Page

from ocyan.plugin.roadrunner_bs5.convert import main as convert
from ocyan.plugin.roadrunner_bs5.convert.utils import REGISTERED_BLOCK_TYPES
from ocyan.plugin.roadrunner_bs5.models import RoadrunnerPage

RAW_UPDATE = Template("UPDATE $db_table SET $column = %s WHERE $id_column = %s")


class Command(BaseCommand):
    help = __doc__

    def add_arguments(self, parser):
        parser.add_argument(
            "--pagetype",
            action="append",
            default=[],
            help="process the custom page types ait",
        )
        parser.add_argument(
            "--image-key",
            action="append",
            default=["image", "product", "category", "template"],
            help="Key of fields that contain id's of images",
        )
        parser.add_argument(
            "--richtext-key",
            action="append",
            default=[
                "block_text",
                "tab_content",
                "html",
                "caption",
                "card_content",
                "modal_content",
                "panel_content",
                "media_content",
                "well_content",
                "body",
                "content",
                "text",
                "paragraph",
            ],
            help="Key of fields that contain richtext data",
        )
        parser.add_argument("--debug", default=False, action="store_true")

    def handle(self, *args, **options):
        self.stdout.write("Registered block types: %s" % REGISTERED_BLOCK_TYPES)

        if (
            settings.ROADRUNNER_REGISTRY_FUNCTION
            == "ocyan.plugin.roadrunner_bs4.utils.get_roadrunner_block_choices"
        ):
            self.stderr.write(
                "You've got 'ocyan.plugin.roadrunner_bs4.utils.get_roadrunner_block_choices' "
                "configured as the ROADRUNNER_REGISTRY_FUNCTION. Please set the ROADRUNNER_REGISTRY_FUNCTION="
                "'ocyan.plugin.roadrunner_bs5.utils.get_roadrunner_block_choices' in your project settings before runing bs4_to_bs5."
            )
            exit(1)

        # pylint: disable=attribute-defined-outside-init
        self.image_keys = options["image_key"]
        self.richtext_keys = options["richtext_key"]

        try:
            with transaction.atomic():
                self.port_templates()
                for model_name in options["pagetype"]:
                    model = apps.get_model(model_name)
                    self.convert_bs4_fields(model)
                self.handle_replacement()
        except Exception as err:  # pylint: disable=broad-except
            self.stdout.write(
                self.style.ERROR(
                    "Something went wrong while replacing bs4 pages with bs5 pages, so no changes have been commited to the database. Error: %s"
                    % err
                )
            )
            if options["debug"]:
                raise

    def convert_bs4_fields(self, model):
        db_table = model._meta.db_table
        fields = []
        for field in model._meta.get_fields():
            if field.__class__.__name__ == "RoadRunnerField":
                fields.append(field)

        id_column = "id"
        if model._meta.parents:
            (parent_pointer,) = model._meta.parents.values()
            id_column = parent_pointer.column

        for instance in model.objects.only(
            "id", "title", *[field.name for field in fields]
        ):
            for field in fields:
                with connection.cursor() as cursor:
                    data = self.convert_bs4_to_bs5(getattr(instance, field.name))
                    query = RAW_UPDATE.substitute(
                        db_table=db_table, column=field.column, id_column=id_column
                    )
                    cursor.execute(query, [data, instance.id])

    def port_templates(self):
        BS4Template = apps.get_model("roadrunner.Template")

        with connection.cursor() as cursor:
            for template in BS4Template.objects.all():
                cursor.execute(
                    "INSERT INTO rr_template (id, name, content) VALUES (%s, %s, %s)",
                    [
                        template.pk,
                        template.name,
                        self.convert_bs4_to_bs5(template.content),
                    ],
                )

    def handle_replacement(self):
        bs4_base_pages = Page.objects.filter(content_type=self.get_bs4_content_type())
        for bs4_base_page in bs4_base_pages:
            bs4_page = bs4_base_page.specific

            # Here we're inserting a new record into the roadrunner_bs5_roadrunnerpage table
            # with the abstractbasepage_ptr_id set to the one of bs4. We're not using RoadrunnerPage.create()
            # here because this will also create new basepages, which is what we don't want.
            with connection.cursor() as cursor:
                cursor.execute(
                    "INSERT INTO roadrunner_bs5_roadrunnerpage (abstractbasepage_ptr_id, content, noindex) VALUES (%s, %s, FALSE)",
                    [
                        bs4_page.abstractbasepage_ptr_id,
                        self.convert_bs4_to_bs5(bs4_page.content),
                    ],
                )

            # Now on the base type, we'll only have to change the content type to the bs5 content type
            # and now the bs5 page will be read instead!
            bs4_base_page.content_type = self.get_bs5_content_type()
            bs4_base_page.save()

        call_command("fixtree")

    def convert_bs4_to_bs5(self, content):
        try:
            result = convert.bs4_to_bs5(
                content.raw_data,
                image_keys=self.image_keys,
                richtext_keys=self.richtext_keys,
            )
        except:
            self.stderr.write(json.dumps(list(content.raw_data), indent=2))
            raise

        return json.dumps(result, indent=2)

    def get_bs4_content_type(self):
        return ContentType.objects.get(
            app_label="roadrunner_bs4", model="roadrunnerbootstrapfourpage"
        )

    def get_bs5_content_type(self):
        return ContentType.objects.get_for_model(RoadrunnerPage)
