import os
import fnmatch
from mutagen import File as MutagenFile
from mutagen.id3 import ID3, ID3NoHeaderError
from mutagen.flac import FLAC
from mutagen.mp4 import MP4
from mutagen.oggvorbis import OggVorbis
from tqdm import tqdm

from src.helpers import *

music_extensions = ('*.mp3', '*.flac', '*.wav', '*.aac', '*.ogg', '*.m4a')
exclude_directories = ("*mary--*", "*example-word*")


def detect_lyrics(abs_path, ext):
    ext = ext.lower()

    try:
        audio = MutagenFile(abs_path)
        if not audio or not audio.tags:
            return False

        # MP3-specific SYLT/USLT detection
        if ext == ".mp3" and isinstance(audio, ID3):
            return bool(audio.getall("SYLT") or audio.getall("USLT"))

        # FLAC/OGG/OPUS lyrics detection
        for key, value in audio.tags.items():
            k = key.upper()
            if k in {"LYRICS", "LRC", "SYNCEDLYRICS"} and value:
                return True

        # Generic fallback
        for key in audio.tags.keys():
            if "LYRIC" in key.upper():
                return True

    except Exception:
        return False

    return False


def get_artist(tags, fallback=None) -> list :
    # FLAC / OGG / general multiple artist fields
    artist_fields = ["ARTISTS", "TXXX:ARTISTS", "©ART", "artist"]
    for field in artist_fields:
        if field in tags and tags[field]:
            values = tags.get(field)
            if not isinstance(values, list):
                return values.text
            return values

    # M4A
    if '\xa9ART' in tags and tags['\xa9ART']:
        values = tags['\xa9ART']
        return values

    if tags.get("TPE1"):
        text = getattr(tags.get("TPE1"), "text", None)
        if text:
            return text

    return fallback

def safe_tag_get(tags, key, index=0):
    try:
        value = tags.get(key, None)
        if isinstance(value, list):
            # filter out None/empty, convert to str
            value = [str(v) for v in value if v]
            return value[index] if len(value) > index else fallback
        return str(value)
    except (KeyError, ValueError, TypeError):
        return None

def get_title(tags):
    ids = ["TIT2", "TITLE", "title", "\xa9nam"]
    for key in ids:
        val = safe_tag_get(tags, key)
        if val and val != "None":
            return val
    return None

def get_album(tags):
    ids = ["TALB", "album", "\xa9alb"]
    for key in ids:
        val = safe_tag_get(tags, key)
        if val and val != "None":
            return val
    return None

def update_local_library(root_dir, output_dir):
    set_workdir(output_dir / "local")

    old_library = get_data("local_library")
    known_paths = {track['path']: track_id for track_id, track in old_library.items()}
    new_library = {}

    song_id = '0'

    def get_new_song_id(prev_id, song_path):
        if song_path in known_paths:
            return int(known_paths[song_path])
        while (prev_id in old_library) or (prev_id in new_library):
            prev_id = str(int(prev_id) + 1)
        return prev_id

    for dir_path, dir_names, filenames in os.walk(root_dir):
        dir_names[:] = [
            d for d in dir_names
            if not any(fnmatch.fnmatch(d, exclude) for exclude in exclude_directories)
        ]

        filtered_filenames = [
            filename for filename in filenames
            if any(fnmatch.fnmatch(filename, ext) for ext in music_extensions)
        ]

        for filename in filtered_filenames:
            song_name, track_type = os.path.splitext(filename)
            relative_path = os.path.relpath(os.path.join(dir_path, filename), root_dir)
            song_id = get_new_song_id(song_id, relative_path)

            new_library[song_id] = {
                "name": None,
                "path": relative_path,
                "type": track_type,
                "artists": None,
                "album": None,
                "duration": None
            }

    to_remove = []

    with tqdm(total=len(new_library.items()), desc='Indexing local library') as process_bar:
        for track_id, track in new_library.items():
            process_bar.update(1)

            abs_path = os.path.join(root_dir, track['path'])
            track["has_lyrics"] = detect_lyrics(abs_path, track['type'].lower())

            try:
                audio = MutagenFile(abs_path)
                if audio and audio.tags:
                    tags = audio.tags

                    track['artists'] = get_artist(tags)
                    track['name'] = get_title(tags)
                    track['album'] = get_album(tags)

                    if not len(track['artists']) or not track['name'] or not track['album']:
                        print(track)
                        raise "Cannot index track"

                else:
                    print(track)
                    to_remove.append(track_id)
                    # raise "Cannot index track"

            except Exception as e:
                print(e)
                print(abs_path)
                raise e


    for rem in to_remove:
        new_library.pop(rem)

    save_data(new_library, "local_library")
