#!/usr/bin/env python
# -*- coding: utf-8 -*-
# vim: ai ts=4 sts=4 et sw=4
# Written by Alan Viars  - This software is public domain

import json
import sys
from baluhn import verify

from pdt.pjson.validate_basic import validate_basic_dict
from pdt.pjson.validate_addresses import validate_address_list
from pdt.pjson.validate_licenses import validate_license_list
from pdt.pjson.validate_taxonomies import validate_taxonomy_list
from pdt.pjson.validate_identifiers import validate_identifier_list
from pdt.pjson.validate_other_names import validate_other_name_list
from pdt.pjson.validate_affiliations import validate_affiliation_list

#from validate_basic import validate_basic_dict
#from validate_addresses import validate_address_list
#from validate_licenses import validate_license_list
#from validate_taxonomies import validate_taxonomy_list
#from validate_identifiers import validate_identifier_list
#from validate_other_names import validate_other_name_list
#from validate_affiliations import validate_affiliation_list

LUHN_PREFIX = "80840"


def validate_pjson(j, action):
    """
    Input a JSON object and an action. return a list of errors. If error list
    is empty then the file is valid.
    """
    action = action.lower()

    errors = []
    warnings = []
    response = {"errors": [], "warnings": []}

    # Check the action
    if action not in ("create", "update", "public"):
        error = "action must be create, update, or public."
        errors.append(error)
        response["errors"] = errors
        return response

    # Does the string contain JSON
    try:
        d = json.loads(j)
    except:
        error = "The string did not contain valid JSON."
        errors.append(error)
        response["errors"] = errors
        return response

    # Is it a dict {} (JSON object equiv)?
    if not isinstance(d, type({})):
        error = "The JSON string did not contain a JSON object i.e. {}."
        errors.append(error)
        response["errors"] = errors
        return response

    # Does it contain the top-level enumeration_type
    if "enumeration_type" not in d and action != "public":
        error = "The JSON object does not contain an enumeration_type."
        errors.append(error)
        response["errors"] = errors
        return response

    # Check for deactivation
    if "number" in d and not d.get(
        "enumeration_type",
            "") and action == "public":
        warning = "This appears to be a deactivated NPI. No information is available for deactivated NPIs."
        warnings.append(warning)
        response["warnings"] = warnings
        return response

    # Is the enumeration_type a valid?
    if d["enumeration_type"] not in ("NPI-1", "NPI-2", "OEID", "HPID"):
        error = "enumeration_type must be one of these: ('NPI-1', 'NPI-2', 'OEID', 'HPID')"
        errors.append(error)
        response["errors"] = errors
        return response

    # If a number is present we assume this is an update.
    if "number" not in d:
        number = None
    else:
        number = d['number']

    if action == "create" and number:
        warning = "enumeration number is generated by CMS. The provided value will be ignored."
        warnings.append(warning)

    if action == "update" and not number:
        error = "enumeration number is required when performaing an update."
        errors.append(error)

    # Check if the Luhn checkdigit makes is correcense.
    if number and d['enumeration_type'] in ('NPI-1', 'NPI-2'):

        prefixed_number = "%s%s" % (LUHN_PREFIX, number)
        luhn_verified = verify(prefixed_number)
        if not luhn_verified:
            warning = "The number %s did not pass Luhn algorithm check digit sanitiy check." % (
                number)
            warnings.append(warning)

    # Check for errors in the basic section
    basic_errors, basic_warnings = validate_basic_dict(d.get('basic', {}),
                                                       d.get(
                                                           'enumeration_type'),
                                                       action, number)

    # Check for errors in the addresses
    address_errors = validate_address_list(
        d.get('addresses', ()), d.get('enumeration_type'))

    # Check for errors in the license section

    if 'licenses' in d:
        license_errors = validate_license_list(
            d.get('licenses', []), d.get('enumeration_type'), action)
    else:
        license_errors = []

    taxonomy_errors = validate_taxonomy_list(
        d.get(
            'taxonomies', ()), d.get(
            'enumeration_type', ()), d.get(
                'licenses', []), d.get(
                    'taxonomy_licenses', []), d.get(
                        'basic', {}).get(
                            'sole_proprietor', "NO"), action)

    if 'identifiers' in d:
        identifier_errors = validate_identifier_list(
            d.get('identifiers', []), d.get('enumeration_type'))
    else:
        identifier_errors = []

    if 'other_names' in d:
        other_names_errors = validate_other_name_list(d.get('other_names', []),
                                                      d.get(
                                                          'enumeration_type'),
                                                      d.get('basic', {}))
    else:
        other_names_errors = []

    affiliation_errors, affiliation_warnings = validate_affiliation_list(
        d.get('affiliations', []), d.get('enumeration_type'))

    errors = errors + basic_errors + other_names_errors + address_errors + \
        license_errors + taxonomy_errors + identifier_errors + affiliation_errors
    warnings = warnings + basic_warnings
    response["errors"] = errors
    response["warnings"] = warnings
    return response


if __name__ == "__main__":

    # Get the file from the command line
    if len(sys.argv) < 3:
        print("You must supply a a ProviderJSON file to validate and an action")
        print("Usage: validate-pjson [ProivderJSON] [update|create]")
        sys.exit(1)
    else:
        pjson_file = sys.argv[1]
        action = sys.argv[2]

    if action.lower() not in ("create", "update", "public"):
        print("You must supply an action of either create or update.")
        print("Usage: validate-pjson [ProivderJSON] [update|create|public]")
        sys.exit(1)

    # Try to open the file
    try:
        fh = open(pjson_file, 'r')

        j = fh.read()

        # Validate the provider JSON content
        errors = validate_pjson(j, action)
        # Print the erros and warings as JSON to stout.
        errors_json = json.dumps(errors, indent=4)
        print(errors_json)
    except IOError:
        print("Could not open file %s." % (pjson_file))
        sys.exit(1)
