#!/usr/bin/env python3
# encoding: utf-8
#
# ** header v3.0
# This file is a part of the CaosDB Project.
#
# Copyright (C) 2022 Alexander Schlemmer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# ** end header
#

from caoscrawler.macros import defmacro_constructor, macro_constructor
from caoscrawler.macros.macro_yaml_object import macro_store
from caoscrawler.crawl import Crawler

from tempfile import NamedTemporaryFile

import yaml
import pytest


@pytest.fixture
def register_macros():
    yaml.SafeLoader.add_constructor("!defmacro", defmacro_constructor)
    yaml.SafeLoader.add_constructor("!macro", macro_constructor)


@pytest.fixture
def macro_store_reset():
    macro_store.clear()


def _temp_file_load(txt: str):
    """
    Create a temporary file with txt and load the crawler
    definition using load_definition from Crawler.
    """
    definition = None
    with NamedTemporaryFile() as f:
        f.write(txt.encode())
        f.flush()
        c = Crawler()
        definition = c.load_definition(f.name)
    return definition


def test_macros(register_macros, macro_store_reset):
    dat = yaml.load("""
defs:
- !defmacro
  name: test
  params:
    a: 2
    b: bla
    c: $variable
  definition:
    expanded_$b:
      blubb: ok$a
      $b: $c

testnode:
  obl: !macro
    test:
      a: 4
      b: yea
""", Loader=yaml.SafeLoader)
    assert dat["testnode"]["obl"]["expanded_yea"]["blubb"] == "ok4"
    assert dat["testnode"]["obl"]["expanded_yea"]["yea"] == "$variable"
    assert "expanded_bla" not in dat["testnode"]["obl"]
    assert "bla" not in dat["testnode"]["obl"]["expanded_yea"]


def test_macro_list_replacment(register_macros, macro_store_reset):
    dat = yaml.load("""
defs:
- !defmacro
  name: test
  params:
    a: 2
    b: bla
    c: $variable
  definition:
    expanded_$b:
      blubb:
      - ok$a
      - $b: $c

testnode:
  obl: !macro
    test:
      a: 4
      b: yea
""", Loader=yaml.SafeLoader)
    assert isinstance(dat["testnode"]["obl"]["expanded_yea"]["blubb"], list)
    assert len(dat["testnode"]["obl"]["expanded_yea"]["blubb"]) == 2
    assert dat["testnode"]["obl"]["expanded_yea"]["blubb"][0] == "ok4"
    assert dat["testnode"]["obl"]["expanded_yea"]["blubb"][1]["yea"] == "$variable"


def test_multi_macros(register_macros, macro_store_reset):
    dat = yaml.load("""
defs:
- !defmacro
  name: test_one
  params: {}
  definition:
    replaced1: ok
- !defmacro
  name: test_two
  params: {}
  definition:
    replaced2: ok
    replaced3: ok

testnode:
    obl: !macro
      test_one:
      test_two:
""", Loader=yaml.SafeLoader)
    assert dat["testnode"]["obl"]["replaced1"] == "ok"
    assert dat["testnode"]["obl"]["replaced2"] == "ok"
    assert dat["testnode"]["obl"]["replaced3"] == "ok"


def test_multi_macros_toplevel(register_macros, macro_store_reset):
    """
    See: https://gitlab.indiscale.com/caosdb/src/caosdb-crawler/-/issues/23
    """
    dat_loader = list(yaml.safe_load_all("""
---
metadata:
  macros:
    - !defmacro
      name: test_one
      params: {}
      definition:
        replaced1: ok
    - !defmacro
      name: test_two
      params: {}
      definition:
        replaced2: ok
        replaced3: ok
---
testnode: !macro
  test_one:
  test_two:
"""))
    assert len(dat_loader) == 2
    dat = dat_loader[1]
    assert dat["testnode"]["replaced1"] == "ok"
    assert dat["testnode"]["replaced2"] == "ok"
    assert dat["testnode"]["replaced3"] == "ok"


def test_load_definition(register_macros, macro_store_reset):
    txt = """
extroot:
  type: Directory
  match: extroot
  subtree:
    SimulationData:
      type: Directory
      match: SimulationData
  """
    # Check whether simple cfoods can be loaded:
    cfood = _temp_file_load(txt)
    assert cfood["extroot"]["subtree"]["SimulationData"]["match"] == "SimulationData"

    cfood = _temp_file_load("""
---
metadata:
  macros:
    - !defmacro
      name: test_one
      params: {}
      definition:
        replaced1: ok
    - !defmacro
      name: test_two
      params:
        match_name: null
      definition:
        type: Directory
        match: $match_name
---
extroot:
  type: Directory
  match: extroot
  subtree:
    SimulationData:
      type: Directory
      match: SimulationData
extroot2: !macro  # test top level macro
  test_one:
extroot3:
  subtree:
    SimulationData: !macro
      test_two:
        match_name: SimulationData
    """)
    assert cfood["extroot"]["subtree"]["SimulationData"]["match"] == "SimulationData"
    assert cfood["extroot2"]["replaced1"] == "ok"
    assert cfood["extroot3"]["subtree"]["SimulationData"]["match"] == "SimulationData"


@pytest.mark.xfail
def test_replace_arbitrary_objects(register_macros, macro_store_reset):
    """
    See: https://gitlab.indiscale.com/caosdb/src/caosdb-crawler/-/issues/24
    """
    dat = yaml.load("""
defs:
- !defmacro
  name: test
  params:
    b: 25
    testvar_list:
    - a
    - $b
    testvar_dict:
      t1: a
      t2: $b
  definition:
    replaced1:
      $b: ok
      c: $testvar_dict
      d: $testvar_list

testnode:
  obl: !macro
    test:
""", Loader=yaml.SafeLoader)
    print(yaml.dump(dat))
    assert dat["testnode"]["obl"]["replaced1"]["c"]["t1"] == "a"
    assert dat["testnode"]["obl"]["replaced1"]["c"]["t2"] == "25"
    assert dat["testnode"]["obl"]["replaced1"]["d"][0] == "a"
    assert dat["testnode"]["obl"]["replaced1"]["d"][1] == "25"