import sys
import cherrypy
import splunk
from splunk.appserver.mrsparkle import *
from splunk.appserver.mrsparkle.controllers.config import ConfigController
from splunk.appserver.mrsparkle.lib.module import *
import splunk.appserver.mrsparkle.lib.util as util
from splunk.appserver.mrsparkle.lib import decorators, filechain
import logging
import splunk.util
import os
import splunk.rest
import splunk.entity as en
import splunk.search as se
import splunk.auth as au
import __main__

import view

logger = logging.getLogger('splunk.appserver.controllers.debug')

class DebugController(BaseController):
    
    def web_debug_conf_check(self):
        if cherrypy.config.get('enableWebDebug') is not True:
            self.raise_403_error()

    """/debug"""

    #@route('/:p=status')
    # @expose_page(methods='GET')
    def status(self, **args):
        '''
        Provides a debug output page for appserver config
        '''
        hasReadPerms = self._hasReadPerms()

        # get overview items
        general = splunk.util.OrderedDict()
        general['Appserver boot path'] = getattr(__main__,'__file__', '<N/A>')
        general['Splunkd URI'] = splunk.mergeHostPath()
        general['Debug Mode'] = __debug__

        # get various dicts
        configController = ConfigController()
        uiConfig = configController.index(asDict=True)

        mm = moduleMapper
        moduleMap = mm.getInstalledModules()

        uiPanels = splunk.util.OrderedDict()
        uiPanels['config'] = uiConfig
        uiPanels['views'] = en.getEntities(view.VIEW_ENTITY_CLASS, namespace=splunk.getDefault('namespace'))
        uiPanels['modules'] = moduleMap
        uiPanels['cherrypy'] = cherrypy.config
        uiPanels['request'] = args
        uiPanels['wsgi'] = cherrypy.request.wsgi_environ

        splunkdPanels = splunk.util.OrderedDict()

        #code to display splunkd debug information as well
        try:
            serverResponse, serverContent = splunk.rest.simpleRequest('/services/debug/status')
            atomFeed = splunk.rest.format.parseFeedDocument(serverContent)
            atomFeed_prim = atomFeed.toPrimitive()
            general['Splunkd time'] = splunk.util.getISOTime(atomFeed.updated)
            general['Splunkd home'] = atomFeed_prim.get('SPLUNK_HOME', '&lt;unknown&gt;')

            for key in atomFeed_prim:
                splunkdPanels[key] = atomFeed_prim[key]

        except splunk.AuthenticationFailed, e:
            splunkdPanels['errors'] = 'The appserver is not authenticated with splunkd; retry login'

        except splunk.SplunkdConnectionException, e:
            splunkdPanels['errors'] = 'The appserver could not connect to the splunkd instance at: %s' % splunk.mergeHostPath()

        except Exception, e:
            splunkdPanels['errors'] = 'Unhandled exception: %s' % str(e)


        cherrypy.response.headers['content-type'] = MIME_HTML

        return self.render_template('debug/status.html', {
            'uiPanels': uiPanels,
            'splunkdPanels': splunkdPanels,
            'appserverTime': splunk.util.getISOTime(),
            'general': general,
            'hasReadPerms': hasReadPerms
        })


    def _hasReadPerms(self):
        '''
        Use services/server/settings as a proxy for read permissions.
        '''

        # NOTE:Due SPL-21113 BETA: unify ACL actions to read/write we cannot use the settings endpoint defer to user admin for now.
        return True if 'admin' == au.getCurrentUser()['name'] else False

        entity = None
        try:
            entity = en.getEntity('/server/', 'settings', namespace=splunk.getDefault('namespace'))
        except Exception, e:
            return False
        if not entity['eai:acl']:
            return False
        if not entity['eai:acl']['perms']:
            return False
        if au.getCurrentUser()['name'] in entity['eai:acl']['perms'].get('read', []):
            return True
        else:
            return False

    def find_unittests(self, path, ext='.html', staticBase=True):
        relativePath = path.strip('/\\')
        extension = ext

        if staticBase:
            testFileDir = os.path.join(cherrypy.config['staticdir'], relativePath)
        else:
            testFileDir = os.path.join(self.get_qunit_base_path(), relativePath)
        logger.debug('Fetching HTML test files from: %s' % testFileDir)

        output = []
        for root, dirs, files in os.walk(testFileDir):
            for f in files:
                if f.endswith(extension):
                    base = root.replace(testFileDir, '')
                    if len(base) > 0:
                        # strip the leading slash and add the file name
                        output.append(base[1:] + '/' + f)
                    else:
                        output.append(f)
        return output

    def get_qunit_base_path(self):
        return util.make_absolute(cherrypy.config.get('templates', 'share/splunk/search_mrsparkle/templates'))

    @route('/:p=qunit_tests')
    def qunit_unittests(self, **kwargs):
        self.web_debug_capability_check()
        tests = self.find_unittests('/testing/qunit', staticBase=False)
        test_paths = [(test[0:-len('.html')], self.make_url(['debug', 'qunit', test[0:-len('.html')]])) for test in tests]
        return self.render_json(test_paths)

    @route('/:p=qunit/*path')
    @expose_page()
    def qunit(self, path, **kwargs):
        '''
        Serves up qunit tests, path is relative to the /templates/testing/qunit directory.
        '''
        self.web_debug_capability_check() 

        template_info = {
            'host': cherrypy.request.headers.get('Host', '')
        }

        template_info['test_url'] = self.make_url(['debug', 'qunit', path])
        if path.endswith('.html'):
            return self.render_template('testing/qunit/%s' % path, template_info)
        else:
            return self.render_template('testing/qunit/%s.html' % path, template_info)

    @route('/:p=reset')
    def reset(self, **kwargs):
        '''
        Resets the user space to a clean state; usually used for testingm
        '''
        self.web_debug_capability_check()
        has_perms = True if 'admin'==au.getCurrentUser()['name'] else False
        jobs_cancelled = []
        if has_perms and cherrypy.request.method=='POST':
            jobs = se.listJobs()
            for job in jobs:
                try:
                    j = se.getJob(job['sid'])
                    j.cancel()
                    jobs_cancelled.append(job['sid'])
                except splunk.ResourceNotFound:
                    continue
        return self.render_template('debug/reset.html', {
            'has_perms': has_perms,
            'method': cherrypy.request.method,
            'jobs_cancelled': jobs_cancelled
        })

    @expose_page(must_login=False, methods=['GET', 'POST'])
    def echo(self, **kw):
        '''echos incoming params'''
        self.web_debug_conf_check()
        output = {
            'headers': cherrypy.request.headers,
            'params': cherrypy.request.params
        }

        return self.render_template('debug/echo.html', output)


    @expose_page()
    def refresh(self, entity=None, **kwargs):
        '''
        Forces a refresh on splunkd resources

        This method calls a splunkd refresh on all registered EAI handlers that
        advertise a reload function.  Alternate entities can be specified by appending
        them via URI parameters.  For example,

            http://localhost:8000/debug/refresh?entity=admin/conf-times&entity=data/ui/manager

        will request a refresh on only 'admin/conf-times' and 'data/ui/manager'.

        1) not all splunkd endpoints support refreshing.
        2) auth-services is excluded from the default set, as refreshing that system will
           logout the current user; use the 'entity' param to force it
        '''
        method = cherrypy.request.method
        if method == 'GET':
            return """<html><body>
            <form method="post">
            <input type="hidden" name="splunk_form_key" value="%s">
            <input type="submit" value="Refresh">
            </form></body></html>""" % util.getFormKey(False)
        elif method == 'POST':
            # get auto-list of refreshable EAI endpoints
            self.web_debug_capability_check()
            allEndpoints = en.getEntities('admin', namespace="search")
            eligibleEndpoints = {}

            for name in allEndpoints:
                for link in allEndpoints[name].links:
                    if link[0] == '_reload':
                        logger.debug('FOUND reload for %s' % name)
                        eligibleEndpoints[name] = allEndpoints[name]
                        break

            if isinstance(entity, list):
                entityPaths = entity
            elif isinstance(entity, basestring):
                entityPaths = [entity]
            else:
                # seed manual endpoints
                entityPaths = [
                    'admin/conf-times',
                    'data/ui/manager',
                    'data/ui/nav',
                    'data/ui/views'
                ]

                # add capable endpoints
                for name in sorted(eligibleEndpoints.keys()):
                    if name in ['auth-services']: # refreshing auth causes logout
                        continue
                    if sys.platform == 'win32' and name == 'fifo':
                        # splunkd never loads FIFO on windows, but advertises it anyway
                        continue
                    entityPaths.append('%s/%s' % (allEndpoints[name].path, allEndpoints[name].name))


            cherrypy.response.headers['content-type'] = MIME_TEXT

            output = ['Entity refresh control page']
            output.append('=' * len(output[0]))
            output.append("'''")
            output.append(self.refresh.__doc__.strip())
            output.append("'''")
            output.append('')

            # call refresh on each
            for path in entityPaths:
                try:
                    en.refreshEntities(path, namespace='search', owner='nobody')
                    output.append(('Refreshing %s' % path).ljust(40, ' ') + 'OK')
                except Exception, e:
                    logger.exception(e)
                    msg = e
                    if hasattr(e, 'extendedMessages') and e.extendedMessages:
                        msg = e.extendedMessages[0]['text']
                    output.append(('Refreshing %s' % path).ljust(43, ' ') + e.__class__.__name__ + ' ' + unicode(msg))

            output.append('DONE')
            return '\n'.join(output)


    @expose_page(must_login=False, verify_sso=False)
    def sso(self):
        self.web_debug_conf_check()
        import socket

        enabled = _("No")
        proxy_to_cherrypy = _('SSO is not enabled.')
        if util.in_sso_mode():
            enabled = _("Yes")
            if cherrypy.request.remote.ip not in splunk.util.stringToFieldList(cherrypy.config.get('trustedIP')):
                proxy_to_cherrypy = _("No. SSO will not be used to authenticate this request.")
            else:
                proxy_to_cherrypy = _("Yes. SSO will be used to authenticate this request.")

        remote_user_header_name = cherrypy.request.config.get('remoteUser') or decorators.DEFAULT_REMOTE_USER_HEADER

        remote_user = cherrypy.request.headers.get(remote_user_header_name)
        if remote_user == None:
            remote_user = _("Not set. SSO may not be enabled or you may not be accessing Splunk via your proxy server.")

        server_info = splunk.entity.getEntity('/server', 'info', namespace=None, owner='anon')
        sso_mode = cherrypy.request.config.get(decorators.SPLUNKWEB_SSO_MODE_CFG)

        output = {
            'host_name': socket.gethostname(),
            'host_ip': socket.gethostbyname(socket.gethostname()),
            'port': cherrypy.config.get('httpport'),
            'sso_enabled': enabled,
            'splunkweb_trusted_ip': cherrypy.config.get('trustedIP') or _('Not set. To enable try configuring the trustedIP setting in web.conf.'),
            'splunkd_trusted_ip': cherrypy.config.get('splunkdTrustedIP') or _('Not set. To enable try configuring the trustedIP setting in server.conf.'),
            'splunkweb_trusted_ip_matches': proxy_to_cherrypy,
            'splunkweb_remote_ip': cherrypy.request.remote.ip,
            'remote_user': remote_user,
            'remote_user_header_name': remote_user_header_name,
            'headers': cherrypy.request.headers,
            'sso_mode': sso_mode
        }

        return self.render_template('debug/sso.html', output)

    @expose_page()
    def clear_cache(self, **unused):
        self.web_debug_capability_check()
        if cherrypy.request.method == 'POST':
            filechain.clear_cache()
            return 'Cache clear requested.'

        return '''
            <html><form method="POST">
            <input type="hidden" name="splunk_form_key" value="%s"/>
            <button type="submit">Clear minification cache</button>
            </form></html>
        ''' % util.getFormKey(False)


