#!/usr/bin/env python3

# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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.

# utility to check mappings, report differences and create missing mappings

#  ** INITIAL VERSION, liable to change **

import argparse
import sys
import yaml
from plugins.elastic import Elastic # pylint: disable=no-name-in-module

if sys.version_info < (3, 8):
    print("This script requires Python 3.8 or higher in order to work!")
    sys.exit(-1)

# the desired mappings
mapping_file = yaml.safe_load(open("mappings.yaml", "r"))

elastic = Elastic()
major = elastic.engineMajor()
if major != 7:
    print("This script requires ElasticSearch 7 API in order to work!")
    sys.exit(-1)

parser = argparse.ArgumentParser(description="Command line options.")
parser.add_argument(
    "--create",
    dest="create",
    action="store_true",
    help="Create the missing mapping(s)",
)
parser.add_argument(
    "--shards",
    dest="shards",
    type=int,
    help="Create the missing indices",
)
parser.add_argument(
    "--replicas",
    dest="replicas",
    type=int,
    help="Create the missing indices",
)
parser.add_argument('names', nargs='*')
args = parser.parse_args()

def descend(hsh, mapping, parent):
    for k,v in mapping.items():
        keys=parent.copy()
        keys.append(k)
        if 'properties' in v:
            descend(hsh, v['properties'], keys)
        else:
            key = ".".join(keys)
            hsh[key] = v

def check_mapping(index):
    # expected mappings
    mappings_expected = mapping_file[index]['properties']

    index_name = elastic.index_name(index)

    # Check that index exists
    if not elastic.indices.exists(index_name):
        if args.shards and args.replicas is not None:
            print("Creating index")
            settings = {"number_of_shards": args.shards, "number_of_replicas": args.replicas}
            elastic.indices.create(
              index=index_name, body={"mappings": mapping_file[index], "settings": settings}
            )
            print("Created index!")
            return
        else:
            print("Index not found!")
            print("Specify --shards and --replicas to create the index")
            return

    # actual mappings
    mappings = elastic.indices.get_mapping(index=index_name)[index_name]['mappings']['properties']

    expected = yaml.dump(mappings_expected, sort_keys = True)
    actual = yaml.dump(mappings, sort_keys = True).replace("'true'","true")

    if actual == expected:
        print("Mappings are as expected, hoorah!")
    else:
        print("Mappings differ:")

        exp = dict()
        descend(exp, mappings_expected,[])
        act = dict()
        descend(act, mappings,[])

        for k,v in exp.items():
            if k not in act:
                if v == {'dynamic': True, 'type': 'object'}:
                    print(f' Key {k} is dynamic')
                else:
                    print(f"Expected {k} {v}; not found")
            else:
                if not v == act[k]:
                    print(f"Expected {k} {v},  found {k} {act[k]}")


for maptype in args.names if len(args.names) > 0 else mapping_file.keys():
    print("Checking " + maptype)
    check_mapping(maptype)
