import logging

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

logger = logging.getLogger(__name__)


class Command(BaseCommand):
    help = "Enrich artist and album metadata (covers, thumbnails) from MusicBrainz and TheAudioDB"

    def add_arguments(self, parser):
        parser.add_argument(
            "--force",
            action="store_true",
            help="Overwrite existing cover image and metadata",
        )
        parser.add_argument(
            "--dry-run",
            action="store_true",
            help="Show what would be done without making changes",
        )
        parser.add_argument(
            "--artists",
            action="store_true",
            help="Only process artists",
        )
        parser.add_argument(
            "--albums",
            action="store_true",
            help="Only process albums",
        )
        parser.add_argument(
            "--needs-metadata",
            action="store_true",
            help="Only process items missing metadata or with broken images",
        )

    def _has_broken_image(self, obj, field_name: str) -> bool:
        field = getattr(obj, field_name, None)
        if not field or not field.name:
            return False
        try:
            return not field.storage.exists(field.name)
        except Exception:
            return True

    def handle(self, *args, **options):
        from music.models import Album, Artist

        force = options["force"]
        dry_run = options["dry_run"]
        only_artists = options["artists"]
        only_albums = options["albums"]
        needs_metadata = options["needs_metadata"]

        if not only_artists and not only_albums:
            only_artists = only_albums = True

        updated_total = 0
        errors_total = 0

        if only_artists:
            updated_total += self._process_artists(force, dry_run, needs_metadata)
        errors_total = 0  # reset per section

        if only_albums:
            updated_total += self._process_albums(force, dry_run, needs_metadata)

        self.stdout.write(
            self.style.SUCCESS(f"\nDone! {updated_total} items processed")
        )

    def _get_artists(self, needs_metadata: bool):
        from music.models import Artist

        qs = Artist.objects.all()
        if needs_metadata:
            qs = qs.filter(
                models.Q(theaudiodb_id__isnull=True)
                | models.Q(theaudiodb_id="")
                | models.Q(thumbnail__isnull=True)
                | models.Q(thumbnail="")
            )

        broken = []
        if needs_metadata:
            broken_qs = Artist.objects.exclude(
                models.Q(thumbnail__isnull=True) | models.Q(thumbnail=""),
            )
            for artist in broken_qs.iterator():
                if self._has_broken_image(artist, "thumbnail"):
                    broken.append(artist)

        return list(qs) + broken

    def _get_albums(self, needs_metadata: bool):
        from music.models import Album

        qs = Album.objects.all()
        if needs_metadata:
            qs = qs.filter(
                models.Q(cover_image__isnull=True)
                | models.Q(cover_image="")
                | models.Q(cover_image="default-image-replace-me")
            )

        broken = []
        if needs_metadata:
            broken_qs = Album.objects.exclude(
                models.Q(cover_image__isnull=True) | models.Q(cover_image=""),
            )
            for album in broken_qs.iterator():
                if self._has_broken_image(album, "cover_image"):
                    broken.append(album)

        return list(qs) + broken

    def _process_artists(self, force, dry_run, needs_metadata):
        from music.models import Artist

        artists = self._get_artists(needs_metadata) if needs_metadata else list(Artist.objects.all())
        total = len(artists)
        self.stdout.write(f"Processing {total} artists")

        if dry_run:
            for artist in artists:
                has_tadb = bool(artist.theaudiodb_id)
                has_thumb = bool(artist.thumbnail)
                thumb_broken = self._has_broken_image(artist, "thumbnail")
                status = f"theaudiodb_id={'✓' if has_tadb else '✗'}"
                if thumb_broken:
                    status += ", thumbnail=BROKEN"
                elif has_thumb:
                    status += ", thumbnail=✓"
                else:
                    status += ", thumbnail=✗"
                self.stdout.write(f"  [DRY RUN] Would fix {artist.name} ({status})")
            return 0

        updated = 0
        errors = 0
        for artist in artists:
            try:
                with transaction.atomic():
                    artist.fix_metadata(
                        force_update=force or self._has_broken_image(artist, "thumbnail")
                    )
                updated += 1
                self.stdout.write(f"  [ARTIST {updated}/{total}] {artist.name}")
            except Exception as e:
                errors += 1
                self.stdout.write(
                    self.style.ERROR(f"  Error updating artist {artist.name}: {e}")
                )

        self.stdout.write(
            self.style.SUCCESS(f"\nArtists done! {updated} updated, {errors} errors")
        )
        return updated

    def _process_albums(self, force, dry_run, needs_metadata):
        from music.models import Album

        albums = self._get_albums(needs_metadata) if needs_metadata else list(Album.objects.all())
        total = len(albums)
        self.stdout.write(f"Processing {total} albums")

        if dry_run:
            for album in albums:
                has_cover = bool(album.cover_image)
                cover_broken = self._has_broken_image(album, "cover_image")
                if cover_broken:
                    status = "cover=BROKEN"
                elif has_cover:
                    status = "cover=✓"
                else:
                    status = "cover=✗"
                self.stdout.write(f"  [DRY RUN] Would fix {album.name} ({status})")
            return 0

        updated = 0
        errors = 0
        for album in albums:
            try:
                with transaction.atomic():
                    if self._has_broken_image(album, "cover_image") or force:
                        album.fetch_artwork(force=True)
                    else:
                        album.fix_metadata()
                updated += 1
                self.stdout.write(f"  [ALBUM {updated}/{total}] {album.name}")
            except Exception as e:
                errors += 1
                self.stdout.write(
                    self.style.ERROR(f"  Error updating album {album.name}: {e}")
                )

        self.stdout.write(
            self.style.SUCCESS(f"\nAlbums done! {updated} updated, {errors} errors")
        )
        return updated
