"""Live/Commentary Cleaner Job — finds live, commentary, and interview content in the library."""

import re
from collections import defaultdict

from core.repair_jobs import register_job
from core.repair_jobs.base import JobContext, JobResult, RepairJob
from utils.logging_config import get_logger

logger = get_logger("repair_job.live_commentary_cleaner")

# Keywords that indicate unwanted content types
# Each tuple: (keyword, content_type_label)
#
# Live patterns require clear recording context — the bare `\blive\b` was
# too loose and falsely flagged verb uses like "What We Live For" by
# American Authors or "Live Forever" by Oasis.
_CONTENT_PATTERNS = [
    # Live
    (r'[\(\[]live\b', 'live'),                       # (Live), [Live at ...]
    (r'-\s*live\b', 'live'),                         # Song - Live
    (r'\blive (at|from|in|on|version|session|recording|performance|album|show|tour|concert|edit|cut|take)\b', 'live'),
    (r'\bin concert\b', 'live'),
    (r'\bconcert\b', 'live'),
    (r'\bon stage\b', 'live'),
    (r'\bunplugged\b', 'live'),
    # Commentary
    (r'\bcommentary\b', 'commentary'),
    (r'\bcommented\b', 'commentary'),
    (r'\btrack.?by.?track\b', 'commentary'),
    # Interview
    (r'\binterview\b', 'interview'),
    (r'\binterlude\b', 'interview'),
    (r'\bskit\b', 'interview'),
    # Spoken word
    (r'\bspoken\s*word\b', 'spoken_word'),
    (r'\bnarrat(?:ion|ed)\b', 'spoken_word'),
    (r'\bintroduction\b', 'spoken_word'),
    # Acappella
    (r'\ba\s*cappella\b', 'acappella'),
    (r'\bacappella\b', 'acappella'),
]


def _detect_content_type(title, album_title=''):
    """Check title and album for unwanted content keywords. Returns content_type or None."""
    combined = f"{title} {album_title}".lower()
    for pattern, content_type in _CONTENT_PATTERNS:
        if re.search(pattern, combined):
            return content_type
    return None


def _format_type(content_type):
    """Format content type for display."""
    return {
        'live': 'Live',
        'commentary': 'Commentary',
        'interview': 'Interview/Skit',
        'spoken_word': 'Spoken Word',
    }.get(content_type, content_type.title())


@register_job
class LiveCommentaryCleanerJob(RepairJob):
    job_id = 'live_commentary_cleaner'
    display_name = 'Live/Commentary Cleaner'
    description = 'Finds live performances, commentary, interviews, and spoken word content'
    help_text = (
        'Scans your library for tracks and albums that contain live performances, '
        'commentary tracks, interviews, skits, or spoken word content based on '
        'title keywords.\n\n'
        'You can configure which content types to flag using the settings below. '
        'Each finding shows the track, its content type, and the matched keyword.\n\n'
        'Fix action: removes the track from the database and deletes the file. '
        'If all tracks in an album are removed, the empty album is also cleaned up.\n\n'
        'Settings:\n'
        '- Flag Live: Flag live performances and concert recordings\n'
        '- Flag Commentary: Flag commentary and track-by-track content\n'
        '- Flag Interviews: Flag interviews, skits, and interludes\n'
        '- Flag Spoken Word: Flag spoken word, narration, and introductions\n'
        '- Scan Album Titles: Also check album titles (catches "Live at Wembley" albums)\n'
        '- Scope: "tracks" flags individual tracks, "albums" flags entire albums with matching titles'
    )
    icon = 'repair-icon-filter'
    default_enabled = False
    default_interval_hours = 168
    default_settings = {
        'flag_live': True,
        'flag_commentary': True,
        'flag_interviews': True,
        'flag_spoken_word': True,
        'scan_album_titles': True,
        'scope': 'tracks',  # 'tracks' or 'albums'
    }
    auto_fix = False

    def _get_settings(self, context: JobContext) -> dict:
        if not context.config_manager:
            return self.default_settings.copy()
        cfg = context.config_manager.get(f'repair.jobs.{self.job_id}.settings', {})
        merged = self.default_settings.copy()
        merged.update(cfg)
        return merged

    def scan(self, context: JobContext) -> JobResult:
        result = JobResult()

        settings = self._get_settings(context)
        enabled_types = set()
        if settings.get('flag_live', True):
            enabled_types.add('live')
        if settings.get('flag_commentary', True):
            enabled_types.add('commentary')
        if settings.get('flag_interviews', True):
            enabled_types.add('interview')
        if settings.get('flag_spoken_word', True):
            enabled_types.add('spoken_word')

        if not enabled_types:
            return result

        scan_album_titles = settings.get('scan_album_titles', True)
        scope = settings.get('scope', 'tracks')

        conn = None
        try:
            conn = context.db._get_connection()
            cursor = conn.cursor()
            cursor.execute("""
                SELECT t.id, t.title, ar.name, al.title, al.id, al.record_type,
                       t.file_path, t.bitrate, t.duration, t.track_number,
                       al.thumb_url, ar.thumb_url
                FROM tracks t
                LEFT JOIN artists ar ON ar.id = t.artist_id
                LEFT JOIN albums al ON al.id = t.album_id
                WHERE t.title IS NOT NULL AND t.title != ''
                  AND t.file_path IS NOT NULL AND t.file_path != ''
            """)
            tracks = cursor.fetchall()
        except Exception as e:
            logger.error("Error fetching tracks: %s", e, exc_info=True)
            result.errors += 1
            return result
        finally:
            if conn:
                conn.close()

        if not tracks:
            return result

        total = len(tracks)
        if context.update_progress:
            context.update_progress(0, total)
        if context.report_progress:
            context.report_progress(phase=f'Scanning {total} tracks...', total=total)

        # Track which albums we've already flagged (for album scope)
        flagged_album_ids = set()

        for idx, row in enumerate(tracks):
            if context.check_stop():
                return result

            result.scanned += 1

            if idx % 200 == 0:
                if context.report_progress:
                    context.report_progress(
                        scanned=idx, total=total,
                        phase=f'Scanning {idx} / {total}',
                        log_line=f'Checking: {row[1]}',
                        log_type='info'
                    )
                if context.update_progress:
                    context.update_progress(idx, total)

            (track_id, title, artist_name, album_title, album_id,
             album_type, file_path, bitrate, duration, track_number,
             album_thumb, artist_thumb) = row

            # Check track title
            content_type = _detect_content_type(title, '')

            # Check album title if enabled and track title didn't match
            album_matched = False
            if not content_type and scan_album_titles and album_title:
                content_type = _detect_content_type('', album_title)
                if content_type:
                    album_matched = True

            if not content_type:
                continue

            # Skip if this content type isn't enabled
            if content_type not in enabled_types:
                continue

            # Album scope: flag once per album, not per track
            if scope == 'albums' and album_matched and album_id:
                if album_id in flagged_album_ids:
                    continue
                flagged_album_ids.add(album_id)

            type_label = _format_type(content_type)
            match_source = f'album "{album_title}"' if album_matched else f'track "{title}"'

            if context.create_finding:
                try:
                    inserted = context.create_finding(
                        job_id=self.job_id,
                        finding_type='unwanted_content',
                        severity='info',
                        entity_type='album' if (scope == 'albums' and album_matched) else 'track',
                        entity_id=str(album_id if (scope == 'albums' and album_matched) else track_id),
                        file_path=file_path,
                        title=f'{type_label}: {title} by {artist_name or "Unknown"}',
                        description=(
                            f'{type_label} content detected in {match_source}. '
                            f'Album: "{album_title or "Unknown"}" ({album_type or "unknown"} type).'
                        ),
                        details={
                            'track': {
                                'id': track_id,
                                'title': title,
                                'artist': artist_name or '',
                                'album': album_title or '',
                                'album_id': album_id,
                                'album_type': album_type or '',
                                'file_path': file_path,
                                'bitrate': bitrate,
                                'duration': duration,
                                'track_number': track_number,
                            },
                            'content_type': content_type,
                            'type_label': type_label,
                            'album_matched': album_matched,
                            'album_thumb_url': album_thumb or None,
                            'artist_thumb_url': artist_thumb or None,
                        }
                    )
                    if inserted:
                        result.findings_created += 1
                    else:
                        result.findings_skipped_dedup += 1
                except Exception as e:
                    logger.debug("Error creating finding: %s", e)
                    result.errors += 1

        if context.update_progress:
            context.update_progress(total, total)

        logger.info("Live/Commentary cleaner: scanned %d tracks, found %d",
                     result.scanned, result.findings_created)
        return result
