# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

import json
import os
from textwrap import dedent

import helpers  # Import test helpers module.
import mozunit
import pytest

helpers.setup()

from GenerateWebIDLBindings import APIEvent, APIFunction, APINamespace, APIType, Schemas


def test_parse_simple_single_api_namespace(write_jsonschema_fixtures):
    """
    Test Basic loading and parsing a single API JSONSchema:
    - single line comments outside of the json structure are ignored
    - parse a simple namespace that includes one permission, type,
      function and event
    """
    schema_dir = write_jsonschema_fixtures({
        "test_api.json": dedent(
            """
          // Single line comments added before the JSON data are tolerated
          // and ignored.
          [
            {
              "namespace": "fantasyApi",
              "permissions": ["fantasyPermission"],
              "types": [
                  {
                    "id": "MyType",
                    "type": "string",
                    "choices": ["value1", "value2"]
                  }
              ],
              "functions": [
                {
                  "name": "myMethod",
                  "type": "function",
                  "parameters": [
                    { "name": "fnParam", "type": "string" },
                    { "name": "fnRefParam", "$ref": "MyType" }
                  ]
                }
              ],
              "events": [
                {
                  "name": "onSomeEvent",
                  "type": "function",
                  "parameters": [
                     { "name": "evParam", "type": "string" },
                     { "name": "evRefParam", "$ref": "MyType" }
                  ]
                }
              ]
            }
          ]
        """
        ),
    })

    schemas = Schemas()
    schemas.load_schemas(schema_dir, "toolkit")

    assert schemas.get_all_namespace_names() == ["fantasyApi"]

    apiNs = schemas.api_namespaces["fantasyApi"]
    assert isinstance(apiNs, APINamespace)

    # Properties related to where the JSON schema is coming from
    # (toolkit, browser or mobile schema directories).
    assert apiNs.in_toolkit
    assert not apiNs.in_browser
    assert not apiNs.in_mobile

    # api_path_string is expected to be exactly the namespace name for
    # non-nested API namespaces.
    assert apiNs.api_path_string == "fantasyApi"

    # parse the schema and verify it includes the expected types events and function.
    schemas.parse_schemas()

    assert set(["fantasyPermission"]) == apiNs.permissions
    assert ["MyType"] == list(apiNs.types.keys())
    assert ["myMethod"] == list(apiNs.functions.keys())
    assert ["onSomeEvent"] == list(apiNs.events.keys())

    type_entry = apiNs.types.get("MyType")
    fn_entry = apiNs.functions.get("myMethod")
    ev_entry = apiNs.events.get("onSomeEvent")

    assert isinstance(type_entry, APIType)
    assert isinstance(fn_entry, APIFunction)
    assert isinstance(ev_entry, APIEvent)


def test_parse_error_on_types_without_id_or_extend(
    base_schema, write_jsonschema_fixtures
):
    """
    Test parsing types without id or $extend raise an error while parsing.
    """
    schema_dir = write_jsonschema_fixtures({
        "test_broken_types.json": json.dumps([
            {
                **base_schema(),
                "namespace": "testBrokenTypeAPI",
                "types": [
                    {
                        # type with no "id2 or "$ref" properties
                    }
                ],
            }
        ])
    })

    schemas = Schemas()
    schemas.load_schemas(schema_dir, "toolkit")

    with pytest.raises(
        Exception,
        match=r"Error loading schema type data defined in 'toolkit testBrokenTypeAPI'",
    ):
        schemas.parse_schemas()


def test_parse_ignores_unsupported_types(base_schema, write_jsonschema_fixtures):
    """
    Test parsing types without id or $extend raise an error while parsing.
    """
    schema_dir = write_jsonschema_fixtures({
        "test_broken_types.json": json.dumps([
            {
                **base_schema(),
                "namespace": "testUnsupportedTypesAPI",
                "types": [
                    {
                        "id": "AnUnsupportedType",
                        "type": "string",
                        "unsupported": True,
                    },
                    {
                        # missing id or $ref shouldn't matter
                        # no parsing error expected.
                        "unsupported": True,
                    },
                    {"id": "ASupportedType", "type": "string"},
                ],
            }
        ])
    })

    schemas = Schemas()
    schemas.load_schemas(schema_dir, "toolkit")
    schemas.parse_schemas()
    apiNs = schemas.api_namespaces["testUnsupportedTypesAPI"]
    assert set(apiNs.types.keys()) == set(["ASupportedType"])


def test_parse_error_on_namespace_with_inconsistent_max_manifest_version(
    base_schema, write_jsonschema_fixtures, tmpdir
):
    """
    Test parsing types without id or $extend raise an error while parsing.
    """
    browser_schema_dir = os.path.join(tmpdir, "browser")
    mobile_schema_dir = os.path.join(tmpdir, "mobile")
    os.mkdir(browser_schema_dir)
    os.mkdir(mobile_schema_dir)

    base_namespace_schema = {
        **base_schema(),
        "namespace": "testInconsistentMaxManifestVersion",
    }

    browser_schema = {**base_namespace_schema, "max_manifest_version": 2}
    mobile_schema = {**base_namespace_schema, "max_manifest_version": 3}

    write_jsonschema_fixtures(
        {"test_inconsistent_maxmv.json": json.dumps([browser_schema])},
        browser_schema_dir,
    )

    write_jsonschema_fixtures(
        {"test_inconsistent_maxmv.json": json.dumps([mobile_schema])}, mobile_schema_dir
    )

    schemas = Schemas()
    schemas.load_schemas(browser_schema_dir, "browser")
    schemas.load_schemas(mobile_schema_dir, "mobile")

    with pytest.raises(
        TypeError,
        match=r"Error loading schema data - overwriting existing max_manifest_version value",
    ):
        schemas.parse_schemas()


if __name__ == "__main__":
    mozunit.main()
