"""Read and write ID3/FLAC tags in their respective files."""

import json
import logging
import os
import time

from mutagen.id3 import COMM, ID3, ID3NoHeaderError

from flash_air_music.exceptions import CorruptedTargetFile

COMMENT_DESCRIPTION = 'Generated by FlashAirMusic'


def read_stored_metadata(path):
    """Read ID3 comment tag of mp3 file. Parses JSON.

    :param str path: Path to mp3 file to read.

    :return: Metadata.
    :rtype: dict
    """
    log = logging.getLogger(__name__)

    # Open file.
    try:
        id3_tags = ID3(path)
    except FileNotFoundError:
        return dict()
    except ID3NoHeaderError:
        log.warning('Corrupted mp3 file: %s', path)
        return dict()

    # Read comment tag.
    try:
        comment = id3_tags.get('COMM:{}:eng'.format(COMMENT_DESCRIPTION)).text[0]
    except (AttributeError, IndexError):
        log.warning('No comment tag in mp3 file: %s', path)
        return dict()

    # Parse JSON.
    try:
        data = dict(json.loads(comment))
    except (TypeError, ValueError):
        log.warning('Comment tag not JSON in mp3 file: %s', path)
        return dict()

    # Make sure there's no funny business in the JSON data.
    try:
        strict_data = dict(
            source_mtime=int(data['source_mtime']),
            source_size=int(data['source_size']),
            target_mtime=int(data['target_mtime']),
            target_size=int(data['target_size']),
        )
    except (KeyError, ValueError):
        log.warning('Comment tag JSON has missing/invalid data mp3 file: %s', path)
        return dict()

    return strict_data


def write_stored_metadata(song):
    """Write ID3 comment tag to mp3 file. Doesn't change file mtime.

    :raise flash_air_music.exceptions.CorruptedTargetFile: On corrupted ID3 header.

    :param flash_air_music.convert.discover.Song song: Song instance.
    """
    log = logging.getLogger(__name__)

    # Pad file and refresh metadata.
    try:
        ID3(song.target).save(padding=lambda _: 200)
    except ID3NoHeaderError:
        log.error('Corrupted mp3 file: %s', song.target)
        raise CorruptedTargetFile
    song.refresh_live_metadata()

    # Write comment.
    id3 = ID3(song.target)
    id3.add(COMM(desc=COMMENT_DESCRIPTION, encoding=3, lang='eng', text=json.dumps(song.live_metadata)))
    id3.save()

    # Restore mtime.
    mtime = song.live_metadata['target_mtime']
    os.utime(song.target, (time.time(), mtime))
