# 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 Any

from ..model import (
    Entry,
    Expression,
    Markup,
    Message,
    Pattern,
    PatternMessage,
    SelectMessage,
    VariableRef,
)


def entry_to_json(entry: Entry[Message]) -> tuple[str, dict[str, Any]]:
    """
    Represent an Entry as a JSON-serializable value.

    Returns the stringified entry id and a JSON object.
    The JSON Schema of the output is provided as [entry.json](../../../../schemas/entry.json).
    """
    id = ".".join(part.replace(".", r"\.") for part in entry.id)
    json: dict[str, Any] = {}
    if entry.meta:
        json["@"] = list([m.key, m.value] for m in entry.meta)
    if entry.comment:
        json["#"] = entry.comment
    if (
        not entry.properties
        or not isinstance(entry.value, PatternMessage)
        or entry.value.pattern
    ):
        json["="] = message_to_json(entry.value)
    if entry.properties:
        json["+"] = {
            key: message_to_json(prop) for key, prop in entry.properties.items()
        }
    return id, json


def message_to_json(msg: Message) -> list[Any] | dict[str, Any]:
    """
    Represent a Message as a JSON-serializable value.

    The JSON Schema of the output is provided as [message.json](../../../../schemas/message.json).
    """
    json_declarations = {
        name: _expression(expr) for name, expr in msg.declarations.items()
    }
    if isinstance(msg, PatternMessage):
        if not json_declarations:
            return _pattern(msg.pattern)
        return {
            "decl": json_declarations,
            "msg": _pattern(msg.pattern),
        }
    else:
        assert isinstance(msg, SelectMessage)
        return {
            "decl": json_declarations,
            "sel": [sel.name for sel in msg.selectors],
            "alt": [
                {
                    "keys": [
                        key if isinstance(key, str) else {"*": key.value or ""}
                        for key in keys
                    ],
                    "pat": _pattern(pattern),
                }
                for keys, pattern in msg.variants.items()
            ],
        }


def _pattern(pattern: Pattern) -> list[str | dict[str, Any]]:
    return [
        part
        if isinstance(part, str)
        else _markup(part)
        if isinstance(part, Markup)
        else _expression(part)
        for part in pattern
    ]


def _expression(expr: Expression) -> dict[str, Any]:
    json: dict[str, Any] = {}
    if isinstance(expr.arg, str):
        json["_"] = expr.arg
    elif isinstance(expr.arg, VariableRef):
        json["$"] = expr.arg.name
    if expr.function:
        json["fn"] = expr.function
        if expr.options:
            json["opt"] = _options(expr.options)
    if expr.attributes:
        json["attr"] = expr.attributes
    return json


def _markup(markup: Markup) -> dict[str, Any]:
    json: dict[str, Any] = {
        "elem" if markup.kind == "standalone" else markup.kind: markup.name
    }
    if markup.options:
        json["opt"] = _options(markup.options)
    if markup.attributes:
        json["attr"] = markup.attributes
    return json


def _options(options: dict[str, str | VariableRef]) -> dict[str, Any]:
    return {
        name: value if isinstance(value, str) else {"$": value.name}
        for name, value in options.items()
    }
