# Generated by Django 2.2.16 on 2021-04-18 17:15

from django.db import migrations, models
import django.db.models.deletion
from django.core.exceptions import ObjectDoesNotExist

from evennia.utils.ansi import parse_ansi
from server.utils.arx_utils import sub_old_ansi

OLD_BASE_DESC = "Nothing has been written on this yet. '{whelp write{n'"


def convert_descs_to_written_works(apps, schema_editor):
    """
    This iterates over all the Readable typeclassed objects we have,
    and tries to convert their descriptions into WrittenWorks. In
    cases where their descs are built up of templates or translated
    texts, those are converted to WrittenWorks first, and then the
    BookChapters are created to associate the works with the book.

    For descs, a WrittenWork is created, which we'll compare future
    descs against to see if there's duplicates.
    """
    ObjectDB = apps.get_model("objects", "ObjectDB")
    WrittenWork = apps.get_model("templates", "WrittenWork")
    BookChapter = apps.get_model("templates", "BookChapter")
    ChapterSignature = apps.get_model("templates", "ChapterSignature")
    readable = ObjectDB.objects.filter(
        db_typeclass_path="typeclasses.readable.readable.Readable"
    )
    # different mappings
    desc_to_work = {}
    template_to_work = {}
    authors = {}

    def get_author(book_object):
        try:
            author_tuple = book_object.db_attributes.get(db_key="author").db_value
            return get_dbobj_from_tuple(author_tuple)
        except (ObjectDoesNotExist, TypeError, IndexError):
            return None

    def get_dbobj_from_tuple(obj_tuple):
        try:
            obj_pk = obj_tuple[-1]
            if obj_pk in authors:
                return authors[obj_pk]
            else:
                author_obj = ObjectDB.objects.get(id=obj_pk)
                authors[obj_pk] = author_obj
                return author_obj
        except (ObjectDoesNotExist, TypeError, IndexError):
            return None

    def get_desc(book_object):
        try:
            desc_attr = book_object.db_attributes.get(db_key="desc")
        except ObjectDoesNotExist:
            # if they have no desc, it's an empty book. skip it
            return
        # if its desc was that it's a blank book, delete the attribute and skip it
        if desc_attr.db_value == OLD_BASE_DESC:
            desc_attr.delete()
            return
        return desc_attr

    def get_signers(book_object):
        try:
            signed_attr = book_object.db_attributes.get(db_key="signed")
            signers_objects = []
            for tup in signed_attr.db_value:
                obj = get_dbobj_from_tuple(tup)
                if obj:
                    signers_objects.append(obj)
            signed_attr.delete()
            return signers_objects
        except (ObjectDoesNotExist, ValueError, TypeError):
            return []

    def add_work_and_chapter_for_desc(book_object, desc_attr, chapter_num, author_obj):
        # check if work already exists
        body = desc_attr.db_value
        if body in desc_to_work:
            work_obj = desc_to_work[body]
        else:
            work_title = book_object.db_key
            num_matches = WrittenWork.objects.filter(
                title__startswith=book_object.db_key
            ).count()
            if num_matches:
                work_title += f" {num_matches + 1}"
            # create the work_obj for the body
            work_obj = WrittenWork.objects.create(
                body=desc_attr.db_value, title=work_title, author=author_obj
            )
            desc_to_work[body] = work_obj
        # create chapter
        return BookChapter.objects.get_or_create(
            defaults=dict(number=chapter_num),
            written_work=work_obj,
            objectdb=book_object,
        )[0]

    def get_max_chapter_num(book_object):
        agg = book_object.book_chapters.aggregate(max_chapter=models.Max("number"))
        return agg.get("max_chapter", 0) or 0

    readable.update(db_cmdset_storage=None)

    for book in readable:
        chapter_number = 1
        desc = get_desc(book)
        if not desc:
            continue
        templates = book.template_set.all()
        if templates:
            # if we have templates, the desc is assumed it just be a list of templates
            # we'll convert templates to WrittenWorks and add chapters for them
            print(f"Processing templates for {book.db_key} (ID #{book.id})")
            for template in templates:
                if template.id in template_to_work:
                    new_work = template_to_work[template.id]
                elif template.desc in desc_to_work:
                    new_work = desc_to_work[template.desc]
                else:
                    colored_title = sub_old_ansi(template.title)
                    title = parse_ansi(colored_title, strip_ansi=True)
                    if colored_title == title:
                        colored_title = ""
                    # try to get author
                    author = get_author(book)
                    # check for unique title
                    title_matches = WrittenWork.objects.filter(
                        title__startswith=title
                    ).count()
                    if title_matches:
                        title += f" {title_matches + 1}"
                        colored_title += f" {title_matches + 1}"
                    new_work = WrittenWork.objects.create(
                        body=template.desc,
                        title=title,
                        colored_title=colored_title,
                        owner=template.owner,
                        author=author,
                    )
                    template_to_work[template.id] = new_work
                    desc_to_work[template.desc] = new_work
                # add work to chapters for the book
                BookChapter.objects.get_or_create(
                    defaults=dict(number=chapter_number),
                    written_work=new_work,
                    objectdb=book,
                )
                chapter_number += 1
            # get rid of old desc and move on
            desc.delete()
            continue

        # convert books with translated descs
        translations = book.translations.all()
        if translations:
            print(f"Processing translations for {book.db_key} (ID #{book.id})")
            base_title = book.db_key
            chapter_number = get_max_chapter_num(book) + 1
            # desc should be first chapter
            author = get_author(book)
            add_work_and_chapter_for_desc(book, desc, chapter_number, author)
            for translation in translations:
                chapter_number += 1
                if translation.description in desc_to_work:
                    work = desc_to_work[translation.description]
                else:
                    # convert translations into WrittenWorks of appropriate language
                    title = base_title
                    if chapter_number > 1:
                        title += f" Chapter {chapter_number}"
                    work = WrittenWork.objects.create(
                        language=translation.language,
                        body=translation.description,
                        title=title,
                        author=author,
                    )
                    desc_to_work[translation.description] = work
                # add bookchapter for each converted translation
                BookChapter.objects.get_or_create(
                    defaults=dict(number=chapter_number),
                    written_work=work,
                    objectdb=book,
                )
                # get rid of old translation
                translation.delete()
            desc.delete()
            continue
        print(f"Processing book {book.db_key} (ID #{book.id})")
        author = get_author(book)
        chapter = add_work_and_chapter_for_desc(book, desc, 1, author)
        desc.delete()
        signers = get_signers(book)
        for signer in signers:
            ChapterSignature.objects.create(book_chapter=chapter, signer=signer)


class Migration(migrations.Migration):

    dependencies = [
        ("character", "0002_fix_character_dependencies"),
        ("templates", "0003_auto_20191228_1417"),
    ]

    operations = [
        migrations.CreateModel(
            name="BookChapter",
            fields=[
                (
                    "id",
                    models.AutoField(
                        auto_created=True,
                        primary_key=True,
                        serialize=False,
                        verbose_name="ID",
                    ),
                ),
                ("number", models.PositiveSmallIntegerField(default=1)),
                (
                    "objectdb",
                    models.ForeignKey(
                        on_delete=django.db.models.deletion.CASCADE,
                        related_name="book_chapters",
                        to="objects.ObjectDB",
                    ),
                ),
            ],
            options={
                "verbose_name_plural": "Book Chapters",
                "ordering": ("number",),
            },
        ),
        migrations.CreateModel(
            name="WrittenWork",
            fields=[
                (
                    "id",
                    models.AutoField(
                        auto_created=True,
                        primary_key=True,
                        serialize=False,
                        verbose_name="ID",
                    ),
                ),
                ("title", models.CharField(max_length=255)),
                ("colored_title", models.TextField(blank=True)),
                ("body", models.TextField()),
                ("language", models.CharField(blank=True, max_length=255)),
                (
                    "author",
                    models.ForeignKey(
                        blank=True,
                        null=True,
                        on_delete=django.db.models.deletion.SET_NULL,
                        related_name="authored_works",
                        to="objects.ObjectDB",
                    ),
                ),
                (
                    "books",
                    models.ManyToManyField(
                        related_name="contained_written_works",
                        through="templates.BookChapter",
                        to="objects.ObjectDB",
                    ),
                ),
                (
                    "owner",
                    models.ForeignKey(
                        blank=True,
                        null=True,
                        on_delete=django.db.models.deletion.SET_NULL,
                        related_name="written_works",
                        to="character.PlayerAccount",
                    ),
                ),
            ],
            options={
                "verbose_name_plural": "Written Works",
            },
        ),
        migrations.CreateModel(
            name="ChapterSignature",
            fields=[
                (
                    "id",
                    models.AutoField(
                        auto_created=True,
                        primary_key=True,
                        serialize=False,
                        verbose_name="ID",
                    ),
                ),
                (
                    "book_chapter",
                    models.ForeignKey(
                        on_delete=django.db.models.deletion.CASCADE,
                        related_name="signatures",
                        to="templates.BookChapter",
                    ),
                ),
                (
                    "signer",
                    models.ForeignKey(
                        on_delete=django.db.models.deletion.CASCADE,
                        related_name="signatures",
                        to="objects.ObjectDB",
                    ),
                ),
            ],
            options={
                "abstract": False,
            },
        ),
        migrations.AddField(
            model_name="bookchapter",
            name="signers",
            field=models.ManyToManyField(
                related_name="signed_chapters",
                through="templates.ChapterSignature",
                to="objects.ObjectDB",
            ),
        ),
        migrations.AddField(
            model_name="bookchapter",
            name="written_work",
            field=models.ForeignKey(
                on_delete=django.db.models.deletion.CASCADE, to="templates.WrittenWork"
            ),
        ),
        migrations.AlterUniqueTogether(
            name="bookchapter",
            unique_together={("objectdb", "number"), ("objectdb", "written_work")},
        ),
        migrations.RunPython(
            convert_descs_to_written_works, migrations.RunPython.noop, elidable=True
        ),
    ]
