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

"""

import argparse
import os
import shutil
import sys
import tempfile
import urllib.parse

from urllib.parse import urlsplit
from couchapp.commands import push as couchapppush

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.
    """
    # set of options required by the couchapp push command
    # from collections import namedtuple
    # couchOpts = namedtuple("opts", ["export", "output", "force", "no_atomic"])
    # for attribute in ["export", "output", "force", "no_atomic"]:
    #     setattr(couchOpts, attribute, False)
    ### AMR debugging workqueue couchapps, remove it later ###
    #couchOpts.export = True
    #couchOpts.output = "/data/WorkQueue.json"
    ##########################################################
    if not basePath:
        basePath = couchAppRoot(couchAppName)
    print("Installing %s into %s" % (couchAppName, urllib.parse.unquote_plus(couchDBName)))

    couchapppush("%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.parse.quote_plus("%s/fwjrs" % couchDBName).replace("%", "\\%")
    jobDumpName = urllib.parse.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.parse.quote_plus("%s/fwjrs" % wmagentConfig.JobStateMachine.couchDBName)
        jobDumpName = urllib.parse.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)
