"""Put document titles and dates back to exactly what the live site shows.

The first import did two things it should not have: it altered some titles, and
it invented *groups* by bundling every document that happened to share an
upload date, then naming the bundle by guesswork. That produced groups like
"2025 AGM documents" holding the 2023, 2024 and 2025 AGM minutes together —
which reads to an owner as though the site has its facts wrong.

WordPress is the authority here. Every document carries the `wp_id` it was
imported with, so the correction is exact rather than a fuzzy title match.

Export first (the portal never connects to the WordPress database):

    srv-gw db-query --project <wp-project> --format json --rows 500 \
      "SELECT ID, post_title, DATE(post_date) AS post_date, post_status
       FROM wp_posts WHERE post_type='docs'" > data/import/<site>-docs.json

    manage.py resync_from_wordpress --file /data/import/<site>-docs.json --dry-run
    manage.py resync_from_wordpress --file /data/import/<site>-docs.json --ungroup

Only metadata is touched. The stored files are never opened or moved.
"""
import datetime
import html
import json

from django.core.management.base import BaseCommand, CommandError
from django.db import transaction

from documents.models import Collection, Document


class Command(BaseCommand):
    help = "Reset document titles/dates to match the live WordPress site."

    def add_arguments(self, parser):
        parser.add_argument("--file", required=True)
        parser.add_argument("--dry-run", action="store_true")
        parser.add_argument(
            "--ungroup", action="store_true",
            help="Also dissolve every group. The groups were invented by the "
                 "importer and are not in WordPress at all.")

    def handle(self, *args, **options):
        try:
            with open(options["file"]) as handle:
                rows = json.load(handle)
        except (OSError, ValueError) as exc:
            raise CommandError("could not read %s: %s" % (options["file"], exc))

        source = {}
        for row in rows:
            wp_id = str(row.get("ID") or "").strip()
            if wp_id:
                source[wp_id] = row

        renamed = redated = restatused = 0
        missing = []
        changes = []

        with transaction.atomic():
            for doc in Document.objects.exclude(wp_id__isnull=True).exclude(wp_id=""):
                row = source.get(str(doc.wp_id))
                if row is None:
                    missing.append((doc.wp_id, doc.title))
                    continue
                fields = []

                # WordPress stores titles HTML-escaped ("&amp;", "&#8217;").
                # Copying them across verbatim would put the entities back on
                # screen — the exact double-escaping already fixed once here.
                title = html.unescape(row.get("post_title") or "").strip()
                if title and title != doc.title:
                    changes.append("  title  %r -> %r" % (doc.title, title))
                    doc.title = title
                    fields.append("title")
                    renamed += 1

                raw = row.get("post_date")
                when = None
                if raw:
                    text = str(raw).split(" ")[0]
                    try:
                        when = datetime.date.fromisoformat(text)
                    except ValueError:
                        when = None
                if when and when != doc.published_date:
                    changes.append("  date   %s: %s -> %s" % (doc.title, doc.published_date, when))
                    doc.published_date = when
                    fields.append("published_date")
                    redated += 1

                status = (row.get("post_status") or "").strip()
                if status in ("publish", "draft"):
                    published = status == "publish"
                    if published != doc.published:
                        changes.append("  status %s: published=%s" % (doc.title, published))
                        doc.published = published
                        fields.append("published")
                        restatused += 1

                if fields and not options["dry_run"]:
                    doc.save(update_fields=fields)

            ungrouped = groups_removed = 0
            if options["ungroup"]:
                ungrouped = Document.objects.exclude(collection__isnull=True).count()
                groups_removed = Collection.objects.count()
                if not options["dry_run"]:
                    Document.objects.update(collection=None)
                    Collection.objects.all().delete()

            if options["dry_run"]:
                transaction.set_rollback(True)

        for line in changes[:40]:
            self.stdout.write(line)
        if len(changes) > 40:
            self.stdout.write("  ... and %d more" % (len(changes) - 40))

        self.stdout.write(
            "%stitles %d, dates %d, published-flags %d, ungrouped %d docs from %d groups"
            % ("[dry run] " if options["dry_run"] else "",
               renamed, redated, restatused, ungrouped, groups_removed))
        if missing:
            self.stdout.write(
                "not in the export (left alone): %s"
                % ", ".join("%s=%r" % (i, t) for i, t in missing[:6]))
