# Copyright Mozilla Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import annotations

from typing import Callable, cast

from ..formats import Format, UnsupportedFormat, detect_format
from ..formats.dtd.parse import dtd_parse
from ..formats.fluent.parse import fluent_parse
from ..formats.inc.parse import inc_parse
from ..formats.ini.parse import ini_parse
from ..formats.plain_json.parse import plain_json_parse
from ..formats.po.parse import po_parse
from ..formats.properties.parse import properties_parse
from ..formats.webext.parse import webext_parse
from ..model import Message, Resource

android_parse: Callable[..., Resource[Message]] | None
xliff_parse: Callable[[str | bytes], Resource[Message]] | None
try:
    from ..formats.android.parse import android_parse
    from ..formats.xliff.parse import xliff_parse
except ImportError:
    android_parse = None
    xliff_parse = None


def parse_resource(
    input: Format | str | None,
    source: str | bytes | None = None,
    *,
    android_ascii_spaces: bool = False,
    android_literal_quotes: bool = False,
) -> Resource[Message]:
    """
    Parse a Resource from its string representation.

    The first argument may be an explicit Format,
    the file path as a string, or None.
    For the latter two types,
    an attempt is made to detect the appropriate format.

    If the first argument is a string path,
    the `source` argument is optional,
    as the file will be opened and read.
    """
    input_is_file = False
    if source is None:
        if not isinstance(input, str):
            raise TypeError("Source is required if type is not a string path")
        with open(input, mode="rb") as file:
            source = file.read()
            input_is_file = True
    # TODO post-py38: should be a match
    format = input if isinstance(input, Format) else detect_format(input, source)
    if format == Format.dtd:
        return dtd_parse(source)
    elif format == Format.fluent:
        return fluent_parse(source)
    elif format == Format.inc:
        return inc_parse(source)
    elif format == Format.ini:
        return ini_parse(source)
    elif format == Format.plain_json:
        return plain_json_parse(source)
    elif format == Format.po:
        # Workaround for https://github.com/izimobil/polib/issues/170
        return po_parse(cast(str, input) if input_is_file else source)
    elif format == Format.properties:
        return properties_parse(source)
    elif format == Format.webext:
        return webext_parse(source)
    elif format == Format.android and android_parse is not None:
        return android_parse(
            source,
            ascii_spaces=android_ascii_spaces,
            literal_quotes=android_literal_quotes,
        )
    elif format == Format.xliff and xliff_parse is not None:
        return xliff_parse(source)
    else:
        raise UnsupportedFormat(f"Unsupported resource format: {input}")
