#!/usr/bin/env python
"""
wmagent-couchapp-init

"""
from __future__ import print_function, division

import argparse
import os
import shutil
import sys
import tempfile
import urllib

try:
    from urlparse import urlsplit
except ImportError:
    # PY3
    from urllib.parse import urlsplit

from couchapp.commands import push as couchapppush
from couchapp.config import Config

from WMCore.Configuration import loadConfigurationFile
from WMCore.Lexicon import splitCouchServiceURL
from WMCore.WMBase import getWMBASE


def couchAppRoot(couchapp):
    """Return parent path containing couchapp"""
    wmcoreroot = os.path.normpath(os.path.join(getWMBASE(), '..', '..', '..'))
    develPath = os.path.join(getWMBASE(), "src", "couchapps")
    if os.path.exists(os.path.join(develPath, couchapp)):
        return develPath
    elif os.path.exists(os.path.join(wmcoreroot, 'xdata', 'couchapps', couchapp)):
        return os.path.join(wmcoreroot, 'xdata', 'couchapps')
    elif os.path.exists(os.path.join(wmcoreroot, 'data', 'couchapps', couchapp)):
        return os.path.join(wmcoreroot, 'data', 'couchapps')
    raise OSError('Cannot find couchapp: %s' % couchapp)


def installCouchApp(couchUrl, couchDBName, couchAppName, basePath=None):
    """
    _installCouchApp_

    Install the given couch app on the given server in the given database.  If
    the database already exists it will be deleted.
    """
    if not basePath:
        basePath = couchAppRoot(couchAppName)
    print("Installing %s into %s" % (couchAppName, urllib.unquote_plus(couchDBName)))

    couchappConfig = Config()

    couchapppush(couchappConfig, "%s/%s" % (basePath, couchAppName),
                 "%s/%s" % (couchUrl, couchDBName))
    return


def installWorkQueueCouchApp(couchUrl, couchDBName):
    """Workqueue has to install yui to couchapp - handle this here"""
    yui = os.environ.get('YUI_ROOT', '')
    if not yui or not os.path.exists(yui):
        raise RuntimeError("Can't locate YUI, set YUI_ROOT env variable")
    basePath = couchAppRoot('WorkQueue')
    # make a temporary directory to create the couchapp structure
    tempDir = os.path.join(tempfile.mkdtemp(), 'WorkQueue')
    try:
        # copy over main WorkQueue couchapp
        shutil.copytree(os.path.join(basePath, 'WorkQueue'), tempDir)
        # link to required structure from yui
        for yFile in files_needed_from_yui:
            dest = os.path.join(tempDir, 'vendor', 'yui', '_attachments', yFile)
            try:
                os.makedirs(os.path.dirname(dest))
            except OSError:
                pass
            os.symlink(os.path.join(yui, yFile), dest)
        installCouchApp(couchUrl, couchDBName, "WorkQueue", basePath=os.path.dirname(tempDir))
    finally:
        shutil.rmtree(tempDir, ignore_errors=True)


def createCronEntries(crontabLines):
    tempDir = tempfile.mkdtemp()
    cname = '%s/crontab' % tempDir
    os.system("crontab -l > %s" % cname)
    entries = [l.replace('\n', '').strip() for l in open(cname, 'r').readlines()]
    with open(cname, 'a') as astream:
        for line in crontabLines:
            line = line.strip()
            if  line not in entries:
                astream.write(line + '\n')
    os.system("crontab %s" % cname)
    os.remove(cname)
    os.rmdir(tempDir)


def setupCron(couchUrl, couchDBName):
    """
    _setupCron_

    Setup cron jobs to index and compact the JobDump.  Things get interesting as
    the database names have a forward slash in them, which needs to be quoted
    to function properly.  The percent character has a special meaning to cron,
    so we need to employ a hacky sed command to filter out the quoting of the
    quoted slash.
    """
    print("Setting up cron jobs for the job dump.")
    fwjrDumpName = urllib.quote_plus("%s/fwjrs" % couchDBName).replace("%", "\\%")
    jobDumpName = urllib.quote_plus("%s/jobs" % couchDBName).replace("%", "\\%")

    indexJobsUrl = "'%s/%s/_design/JobDump/_view/statusByWorkflowName?limit=1'" % (couchUrl, jobDumpName)
    indexFwjrUrl = "'%s/%s/_design/FWJRDump/_view/outputByJobID?limit=1'" % (couchUrl, fwjrDumpName)
    indexCmd = "echo %s | sed -e 's|\\\\||g' | xargs curl -s -m 1 > /dev/null"

    crontabLines = []
    crontabLines.append("* * * * * %s" % (indexCmd % indexJobsUrl))
    crontabLines.append("* * * * * %s" % (indexCmd % indexFwjrUrl))
    createCronEntries(crontabLines)


def setupCronCompaction(couchUrl, couchDbName):
    """Add a simple couch compaction"""
    compactUrl = "%s/%s/_compact" % (couchUrl, couchDbName)
    compactCmd = "curl -s -H 'Content-Type: application/json' -X POST '%s' > /dev/null"
    cronLine = "0 1 * * * %s" % (compactCmd % compactUrl)
    createCronEntries([cronLine])


files_needed_from_yui = [
    'build/animation/animation-min.js',
    'build/assets/skins/sam/ajax-loader.gif',
    'build/assets/skins/sam/asc.gif',
    'build/assets/skins/sam/autocomplete.css',
    'build/assets/skins/sam/back-h.png',
    'build/assets/skins/sam/back-v.png',
    'build/assets/skins/sam/bar-h.png',
    'build/assets/skins/sam/bar-v.png',
    'build/assets/skins/sam/bg-h.gif',
    'build/assets/skins/sam/bg-v.gif',
    'build/assets/skins/sam/blankimage.png',
    'build/assets/skins/sam/button.css',
    'build/assets/skins/sam/calendar.css',
    'build/assets/skins/sam/carousel.css',
    'build/assets/skins/sam/check0.gif',
    'build/assets/skins/sam/check1.gif',
    'build/assets/skins/sam/check2.gif',
    'build/assets/skins/sam/colorpicker.css',
    'build/assets/skins/sam/container.css',
    'build/assets/skins/sam/datatable.css',
    'build/assets/skins/sam/desc.gif',
    'build/assets/skins/sam/dt-arrow-dn.png',
    'build/assets/skins/sam/dt-arrow-up.png',
    'build/assets/skins/sam/editor-knob.gif',
    'build/assets/skins/sam/editor-sprite-active.gif',
    'build/assets/skins/sam/editor-sprite.gif',
    'build/assets/skins/sam/editor.css',
    'build/assets/skins/sam/header_background.png',
    'build/assets/skins/sam/hue_bg.png',
    'build/assets/skins/sam/imagecropper.css',
    'build/assets/skins/sam/layout.css',
    'build/assets/skins/sam/layout_sprite.png',
    'build/assets/skins/sam/loading.gif',
    'build/assets/skins/sam/logger.css',
    'build/assets/skins/sam/menu-button-arrow-disabled.png',
    'build/assets/skins/sam/menu-button-arrow.png',
    'build/assets/skins/sam/menu.css',
    'build/assets/skins/sam/menubaritem_submenuindicator.png',
    'build/assets/skins/sam/menubaritem_submenuindicator_disabled.png',
    'build/assets/skins/sam/menuitem_checkbox.png',
    'build/assets/skins/sam/menuitem_checkbox_disabled.png',
    'build/assets/skins/sam/menuitem_submenuindicator.png',
    'build/assets/skins/sam/menuitem_submenuindicator_disabled.png',
    'build/assets/skins/sam/paginator.css',
    'build/assets/skins/sam/picker_mask.png',
    'build/assets/skins/sam/profilerviewer.css',
    'build/assets/skins/sam/progressbar.css',
    'build/assets/skins/sam/resize.css',
    'build/assets/skins/sam/simpleeditor.css',
    'build/assets/skins/sam/skin.css',
    'build/assets/skins/sam/slider.css',
    'build/assets/skins/sam/split-button-arrow-active.png',
    'build/assets/skins/sam/split-button-arrow-disabled.png',
    'build/assets/skins/sam/split-button-arrow-focus.png',
    'build/assets/skins/sam/split-button-arrow-hover.png',
    'build/assets/skins/sam/split-button-arrow.png',
    'build/assets/skins/sam/sprite.png',
    'build/assets/skins/sam/sprite.psd',
    'build/assets/skins/sam/tabview.css',
    'build/assets/skins/sam/treeview-loading.gif',
    'build/assets/skins/sam/treeview-sprite.gif',
    'build/assets/skins/sam/treeview.css',
    'build/assets/skins/sam/wait.gif',
    'build/assets/skins/sam/yuitest.css',
    'build/connection/connection-min.js',
    'build/connection/connection_core-min.js',
    'build/container/assets/alrt16_1.gif',
    'build/container/assets/blck16_1.gif',
    'build/container/assets/close12_1.gif',
    'build/container/assets/container-core.css',
    'build/container/assets/container.css',
    'build/container/assets/hlp16_1.gif',
    'build/container/assets/info16_1.gif',
    'build/container/assets/skins/sam/container-skin.css',
    'build/container/assets/skins/sam/container.css',
    'build/container/assets/tip16_1.gif',
    'build/container/assets/warn16_1.gif',
    'build/container/container-min.js',
    'build/container/container_core-min.js',
    'build/datasource/datasource-min.js',
    'build/datatable/assets/datatable-core.css',
    'build/datatable/assets/datatable.css',
    'build/datatable/assets/skins/sam/datatable-skin.css',
    'build/datatable/assets/skins/sam/datatable.css',
    'build/datatable/assets/skins/sam/dt-arrow-dn.png',
    'build/datatable/assets/skins/sam/dt-arrow-up.png',
    'build/datatable/datatable-min.js',
    'build/dragdrop/dragdrop-min.js',
    'build/element/element-min.js',
    'build/fonts/fonts-min.css',
    'build/fonts/fonts.css',
    'build/json/json-min.js',
    'build/layout/assets/layout-core.css',
    'build/layout/assets/skins/sam/layout-skin.css',
    'build/layout/assets/skins/sam/layout.css',
    'build/layout/assets/skins/sam/layout_sprite.png',
    'build/layout/layout-min.js',
    'build/menu/assets/menu-core.css',
    'build/menu/assets/menu.css',
    'build/menu/assets/menu_down_arrow.png',
    'build/menu/assets/menu_down_arrow_disabled.png',
    'build/menu/assets/menu_up_arrow.png',
    'build/menu/assets/menu_up_arrow_disabled.png',
    'build/menu/assets/menubaritem_submenuindicator.png',
    'build/menu/assets/menubaritem_submenuindicator_disabled.png',
    'build/menu/assets/menubaritem_submenuindicator_selected.png',
    'build/menu/assets/menuitem_checkbox.png',
    'build/menu/assets/menuitem_checkbox_disabled.png',
    'build/menu/assets/menuitem_checkbox_selected.png',
    'build/menu/assets/menuitem_submenuindicator.png',
    'build/menu/assets/menuitem_submenuindicator_disabled.png',
    'build/menu/assets/menuitem_submenuindicator_selected.png',
    'build/menu/assets/skins/sam/menu-skin.css',
    'build/menu/assets/skins/sam/menu.css',
    'build/menu/assets/skins/sam/menubaritem_submenuindicator.png',
    'build/menu/assets/skins/sam/menubaritem_submenuindicator_disabled.png',
    'build/menu/assets/skins/sam/menuitem_checkbox.png',
    'build/menu/assets/skins/sam/menuitem_checkbox_disabled.png',
    'build/menu/assets/skins/sam/menuitem_submenuindicator.png',
    'build/menu/assets/skins/sam/menuitem_submenuindicator_disabled.png',
    'build/menu/menu-min.js',
    'build/paginator/assets/paginator-core.css',
    'build/paginator/assets/skins/sam/paginator-skin.css',
    'build/paginator/assets/skins/sam/paginator.css',
    'build/paginator/paginator-min.js',
    'build/progressbar/assets/progressbar-core.css',
    'build/progressbar/assets/skins/sam/back-h.png',
    'build/progressbar/assets/skins/sam/back-v.png',
    'build/progressbar/assets/skins/sam/bar-h.png',
    'build/progressbar/assets/skins/sam/bar-v.png',
    'build/progressbar/assets/skins/sam/progressbar-skin.css',
    'build/progressbar/assets/skins/sam/progressbar.css',
    'build/progressbar/progressbar-min.js',
    'build/reset-fonts-grids/reset-fonts-grids.css',
    'build/resize/assets/resize-core.css',
    'build/resize/assets/skins/sam/layout_sprite.png',
    'build/resize/assets/skins/sam/resize-skin.css',
    'build/resize/assets/skins/sam/resize.css',
    'build/resize/resize-min.js',
    'build/utilities/utilities.js',
    'build/yahoo-dom-event/yahoo-dom-event.js',
    ]


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--skip-cron", dest = "cron",
                        default = True, action = "store_false",
                        help = "Do not install maintenance cron jobs")
    options = parser.parse_args()

    if "WMAGENT_CONFIG" not in os.environ:
        print("The WMAGENT_CONFIG environment variable needs to be set before")
        print("this can run.")
        sys.exit(1)

    wmagentConfig = loadConfigurationFile(os.environ["WMAGENT_CONFIG"])
            
    if hasattr(wmagentConfig, "JobStateMachine") and hasattr(wmagentConfig.JobStateMachine, "couchDBName"):
        fwjrDumpName = urllib.quote_plus("%s/fwjrs" % wmagentConfig.JobStateMachine.couchDBName)
        jobDumpName = urllib.quote_plus("%s/jobs" % wmagentConfig.JobStateMachine.couchDBName)
        installCouchApp(wmagentConfig.JobStateMachine.couchurl, fwjrDumpName, "FWJRDump")
        installCouchApp(wmagentConfig.JobStateMachine.couchurl, jobDumpName, "JobDump")
        installCouchApp(wmagentConfig.JobStateMachine.couchurl, wmagentConfig.JobStateMachine.jobSummaryDBName, "WMStatsAgent")
        installCouchApp(wmagentConfig.JobStateMachine.couchurl, wmagentConfig.JobStateMachine.summaryStatsDBName, "SummaryStats")
        if options.cron:
            setupCron(wmagentConfig.JobStateMachine.couchurl, wmagentConfig.JobStateMachine.couchDBName)

    if hasattr(wmagentConfig, "JobStateMachine") and hasattr(wmagentConfig.JobStateMachine, "configCacheDBName"):
        installCouchApp(wmagentConfig.JobStateMachine.couchurl,
                        wmagentConfig.JobStateMachine.configCacheDBName, "ConfigCache")
        installCouchApp(wmagentConfig.JobStateMachine.couchurl,
                        wmagentConfig.JobStateMachine.configCacheDBName, "GroupUser")

    if hasattr(wmagentConfig, "WorkQueueManager"):
        installWorkQueueCouchApp(wmagentConfig.WorkQueueManager.couchurl, wmagentConfig.WorkQueueManager.dbname)
        installWorkQueueCouchApp(wmagentConfig.WorkQueueManager.couchurl, wmagentConfig.WorkQueueManager.inboxDatabase)

    if hasattr(wmagentConfig, "AsyncTransfer"):
        basePath = "%s/couchapp" % os.environ['ASYNC_ROOT']
        baseWMCorePath = "%s/data/couchapps" % os.environ['ASYNC_ROOT']
        # The format of wmagentConfig.CoreDatabase.connectUrl is http://$COUCH_USER:$COUCH_PASS@$COUCH_HOST_NAME:$COUCH_PORT/asynctransfer_agent
        agent_database = wmagentConfig.CoreDatabase.connectUrl.split('/')[len(wmagentConfig.CoreDatabase.connectUrl.split('/')) - 1 ]
        agent_instance = wmagentConfig.CoreDatabase.connectUrl.split(agent_database)[0]
        installCouchApp(wmagentConfig.AsyncTransfer.couch_instance, wmagentConfig.AsyncTransfer.files_database, "Acquired", basePath)
        installCouchApp(wmagentConfig.AsyncTransfer.couch_instance, wmagentConfig.AsyncTransfer.files_database, "AsyncTransfer", basePath)
        installCouchApp(wmagentConfig.AsyncTransfer.couch_instance, wmagentConfig.AsyncTransfer.files_database, "AsyncTransferErl", basePath)
        installCouchApp(wmagentConfig.AsyncTransfer.couch_instance, wmagentConfig.AsyncTransfer.files_database, "ftscp", basePath)
        installCouchApp(wmagentConfig.AsyncTransfer.couch_instance, wmagentConfig.AsyncTransfer.files_database, "monitor", basePath)
        installCouchApp(wmagentConfig.AsyncTransfer.couch_instance, wmagentConfig.AsyncTransfer.files_database, "Others", basePath)
        installCouchApp(wmagentConfig.Statistics.couch_statinstance, wmagentConfig.Statistics.statitics_database, "stat", basePath)
        installCouchApp(wmagentConfig.DBSPublisher.couch_instance, wmagentConfig.DBSPublisher.files_database, "DBSPublisher", basePath)
        installCouchApp(wmagentConfig.AsyncTransfer.couch_instance, wmagentConfig.AsyncTransfer.config_database, "config", basePath)
        installCouchApp(agent_instance, agent_database, "Agent", baseWMCorePath)
        if options.cron:
            setupCronCompaction(wmagentConfig.AsyncTransfer.couch_instance, wmagentConfig.AsyncTransfer.files_database)
            setupCronCompaction(wmagentConfig.Statistics.couch_statinstance, wmagentConfig.Statistics.statitics_database)
            setupCronCompaction(agent_instance, agent_database)

    if hasattr(wmagentConfig, "Analytics"):
        installCouchApp(wmagentConfig.Analytics.couch_user_monitoring_instance, wmagentConfig.Analytics.user_monitoring_db, "UserMonitoring", basePath)
        if options.cron:
            setupCronCompaction(wmagentConfig.Analytics.couch_user_monitoring_instance, wmagentConfig.Analytics.user_monitoring_db)

    if hasattr(wmagentConfig, "reqmgr"):
        installCouchApp(wmagentConfig.reqmgr.couchUrl, wmagentConfig.reqmgr.workloadDBName, "ReqMgr")
        # which needs to be changed in wmagent for prompt skiming deployment script
        installCouchApp(wmagentConfig.reqmgr.couchUrl, wmagentConfig.reqmgr.wmstatDBName, "WMStats")
        # this means test mode  in wmagent it won't depoly reqmgr with it in production
        # set up  workload summary for test
        if hasattr(wmagentConfig, "WorkloadSummary"):
            installCouchApp(wmagentConfig.WorkloadSummary.couchurl, wmagentConfig.WorkloadSummary.database, "WorkloadSummary")

        if hasattr(wmagentConfig, "ACDC"):
            installCouchApp(wmagentConfig.ACDC.couchurl, wmagentConfig.ACDC.database, "ACDC")
            installCouchApp(wmagentConfig.ACDC.couchurl, wmagentConfig.ACDC.database, "GroupUser")

    else:
        # in case reqmgr is not set but still worklaad Summary need to be initialized (Tier0 test mode case)
        # hacky way to check - assuming production url always use https protocal
        if hasattr(wmagentConfig, "WorkloadSummary") and \
           urlsplit(wmagentConfig.WorkloadSummary.couchurl).scheme == 'http':
            installCouchApp(wmagentConfig.WorkloadSummary.couchurl, wmagentConfig.WorkloadSummary.database, "WorkloadSummary")
        if hasattr(wmagentConfig, "ACDC") and \
           urlsplit(wmagentConfig.WorkloadSummary.couchurl).scheme == 'http':
            installCouchApp(wmagentConfig.ACDC.couchurl, wmagentConfig.ACDC.database, "ACDC")
            installCouchApp(wmagentConfig.ACDC.couchurl, wmagentConfig.ACDC.database, "GroupUser")
    
    if hasattr(wmagentConfig, "Tier0Feeder"):
        installCouchApp(wmagentConfig.JobStateMachine.couchurl, wmagentConfig.Tier0Feeder.requestDBName, "T0Request")
        centralWMStatsURL = wmagentConfig.General.centralWMStatsURL
        # checks whether this is test agent. if T0 agent is setting local wmstats as central it creates the db
        if "localhost:" in centralWMStatsURL:
            couchURL, wmstatsDBName = splitCouchServiceURL(centralWMStatsURL)
            installCouchApp(couchURL, wmstatsDBName, "WMStats")

    if hasattr(wmagentConfig, "General") and hasattr(wmagentConfig.General, "central_logdb_url") and \
            "localhost" in wmagentConfig.General.central_logdb_url:
        couchURL, logDBName = splitCouchServiceURL(wmagentConfig.General.central_logdb_url)
        installCouchApp(couchURL, logDBName, "LogDB")

    sys.exit(0)
