# -*- coding: utf-8 -*-
'''
Lint states and sls files
'''
from __future__ import absolute_import

# Import python libs
import os
import json
import copy
import shutil
import time
import logging
import tarfile
import datetime
import tempfile
from types import NoneType
import re
from voluptuous import *

# Import salt libs
import salt.config
import salt.utils
import salt.state
import salt.payload
from salt.ext.six import string_types
from salt.exceptions import SaltInvocationError

__outputter__ = {
    'validate_sls': 'highstate',
}

log = logging.getLogger(__name__)

def _schema():
    statemods = salt.loader.states(__opts__, __salt__)
    argspecs = salt.utils.argspec_report(statemods)

    specialargs = {
        'name': Coerce(str),
        'names': list,
        'check_cmd': str,
        'listen': list,
        'listen_in': list,
        'onchanges': list,
        'onchanges_in': list,
        'onfail': list,
        'onfail_in': list,
        'onlyif': Coerce(str),
        'order': int,
        'prereq': list,
        'prereq_in': list,
        'require': list,
        'require_in': list,
        'unless': Coerce(str),
        'use': list,
        'watch': list,
        'watch_in': list,
        'formatter': str
    }

    # define voluptuous schema
    schema = {}
    for state, specs in argspecs.iteritems():
        s = specialargs.copy()
        for idx, arg in enumerate(specs['args']):

            # Don't overwrite args from specialargs
            if arg in s:
              break

            try:
                default = specs['defaults'][idx]
            except:
                default = 'nodefault'
            if type(default) == bool:
                stype = bool
            elif type(default) == NoneType:
                stype = Coerce(str)
            else:
                stype = Coerce(type(default))
            s[arg] = stype
        schema[state] = Schema(s)

    return schema


def validate_sls(mods, saltenv='base', test=None, queue=False, env=None, **kwargs):

    schema = _schema()
    data = __salt__['state.show_sls'](mods, saltenv, test, queue, env, kwargs=kwargs)
    ret = []

    # iterate over states
    # TODO: add include, exclude to schema
    #       handle context, defaults better
    prog = re.compile(r'.*\.')
    for id, resource in data.items():
        if id in ['include', 'exclude']:
            break

        # iterate over states
        for module, args in resource.items():

            if module in ['__sls__', '__env__']:
                break

            # find state name, i.e. cmd.run
            match = prog.match(module)
            if match:
                state = module
            else:
                state = "%s.%s" % (module, args.pop(0))

            # check function exists in scema
            if state not in schema:
                ret.append("%s: %s not part of schema" % (file, state))
                break

            # iterate over arguments to make sure they're valid according to our schema
            for arg in args:
                if arg.iterkeys().next() in [ 'context', 'defaults' ]:
                    break
                try:
                    schema[state](arg)
                except Exception as e:
                    ret.append("%s %s: Got %s for %s but %s" % (id, state, arg.itervalues().next(), arg.iterkeys().next(), e.msg))

    return ret

@@ -295,16 +295,64 @@
 import *
+%0Afrom inspect import getargspec%0Aimport importlib
 %0A%0A# Impo
@@ -619,78 +619,200 @@
 _)%0A%0A
+%0A
 def _
+get
 schema(
+state
 ):%0A
+%0A
     
-statemods = salt.loader.states(__opts__, __salt__)%0A
+(module, function) = state.split('.')%0A    try:%0A        package = importlib.import_module(%22salt.states.%25s%22 %25 module)%0A    except:%0A        return False%0A%0A    try:%0A    
     
@@ -822,65 +822,94 @@
 spec
-s
  = 
-salt.utils.argspec_report(statemods)%0A%0A    specialargs
+getargspec(getattr(package, function))%0A    except e:%0A      return False%0A%0A    schema
  = %7B
@@ -1444,276 +1444,78 @@
     
-# define voluptuous schema%0A    schema = %7B%7D%0A    for state, specs in argspecs.iteritems():%0A        s = specialargs.copy()%0A        for idx, arg in enumerate(specs%5B'args'%5D):%0A%0A            # Don't overwrite args from specialargs%0A            if arg in s:%0A              break%0A
+for idx, arg in enumerate(argspec.args):%0A        if arg not in schema:
 %0A   
@@ -1558,23 +1558,24 @@
 t = 
+arg
 spec
-s%5B'
+.
 defaults
 '%5D%5Bi
@@ -1570,18 +1570,16 @@
 defaults
-'%5D
 %5Bidx%5D%0A  
@@ -1858,16 +1858,21 @@
        s
+chema
 %5Barg%5D = 
@@ -1881,42 +1881,8 @@
 ype%0A
-        schema%5Bstate%5D = Schema(s)%0A
 %0A   
@@ -1889,23 +1889,30 @@
  return 
+Schema(
 schema
-%0A
+)
 %0A%0Adef va
@@ -2007,17 +2007,23 @@
 a = 
-_schema()
+%7B%7D%0A    ret = %5B%5D
 %0A   
@@ -2104,29 +2104,16 @@
 =kwargs)
-%0A    ret = %5B%5D
 %0A%0A    # 
@@ -2421,24 +2421,80 @@
 e.items():%0A%0A
+            # Ignore data added by show_%7Bsls,highstate%7D%0A
             
@@ -2805,16 +2805,17 @@
 ts in sc
+h
 ema%0A    
@@ -2838,32 +2838,128 @@
  not in schema:%0A
+                schema%5Bstate%5D =  _getschema(state)%0A                if schema%5Bstate%5D == False:%0A  
                 
@@ -3014,16 +3014,18 @@
 state))%0A
+  
         
@@ -3480,24 +3480,80 @@
 ), e.msg))%0A%0A
+    if len(ret) %3E 0:%0A        __context__%5B'retcode'%5D = 1%0A
     return r

Use inspect.getargparse rather than salt.utils.argspec_report to generate schema as required. salt.utils.argspec_report fails to return all the expected states.