"""Bring the mule-image DB into 100% sync with Nextcloud + filesystem.

Multi-phase one-shot operation invoked via:

    docker exec mulita-backend python scripts/full_refresh.py [--dry-run]

Phases:
  1. Data integrity (sync, ~1s): cleanup_data_integrity dedupes
     SourceRoots / Folders by normalized path and recomputes folder
     photo_count.
  2. Forward scan (async, minutes): walk every active SourceRoot on
     disk, create/update Photo rows for new files, resurrect any
     accidentally-discarded photos whose mtime advanced.
  3. Hard prune (sync, seconds): delete Photo + Folder rows for paths
     that no longer exist on disk under a *mounted* root. Skips
     unmounted roots — matches prune_missing_photos's existing
     refuse-when-empty behavior.
  4. Orphan thumbnail dirs (sync, seconds): remove
     /data/thumbs/{user_id}/{photo_id}/ for any photo_id that's no
     longer in the photos table.

Pass --dry-run to compute counts for phases 3+4 without making changes.
Phases 1 and 2 always run for real — they're idempotent and additive.

Print a structured summary at the end. Exit non-zero on any phase
error; partial completion still surfaces the counts gathered so far.
"""
from __future__ import annotations

import argparse
import asyncio
import logging
import sys

from app.services.cleanup import (
    cleanup_data_integrity,
    prune_missing_photos,
    prune_orphan_thumbnails,
)
from app.tasks.scan import _scan_folder_async
from app.database import AsyncSessionLocal
from app.models.folders import SourceRoot
from sqlalchemy import select


logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s %(levelname)s %(name)s: %(message)s",
)
logger = logging.getLogger("full_refresh")


async def _scan_all_inline() -> int:
    """Scan every active SourceRoot inline (not via celery). Returns the
    number of roots actually walked."""
    import os
    async with AsyncSessionLocal() as session:
        result = await session.execute(
            select(SourceRoot).where(SourceRoot.is_active.is_(True))
        )
        roots = result.scalars().all()

    walked = 0
    for sr in roots:
        if not os.path.exists(sr.path):
            logger.warning("source root path missing, skipping: %s", sr.path)
            continue
        logger.info("scanning %s …", sr.path)
        await _scan_folder_async(sr.path, sr.id, task=None)
        walked += 1
    return walked


async def main(dry_run: bool) -> dict:
    summary: dict = {"dry_run": dry_run}

    logger.info("phase 1: cleanup_data_integrity")
    summary["phase1_cleanup"] = await cleanup_data_integrity()

    logger.info("phase 2: scan_all_source_roots (inline)")
    summary["phase2_scan_roots_walked"] = await _scan_all_inline()

    logger.info("phase 3: prune_missing_photos (dry_run=%s)", dry_run)
    summary["phase3_prune"] = await prune_missing_photos(dry_run=dry_run)

    logger.info("phase 4: prune_orphan_thumbnails (dry_run=%s)", dry_run)
    summary["phase4_orphan_thumbs"] = await prune_orphan_thumbnails(
        dry_run=dry_run,
    )

    return summary


def cli() -> int:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument(
        "--dry-run",
        action="store_true",
        help="Phases 3+4 report counts without making changes",
    )
    args = parser.parse_args()

    try:
        result = asyncio.run(main(dry_run=args.dry_run))
    except Exception:
        logger.exception("full_refresh failed")
        return 1

    import json
    print(json.dumps(result, indent=2, default=str))
    return 0


if __name__ == "__main__":
    sys.exit(cli())
