#!/usr/bin/env python3
"""Open a shell in a tmux window and run a manage command on a remote server.
"""
# Please run the tests:
#   pytest -vvv bin/remote_manage_shell.py
import argparse
import logging
import socket
import subprocess
import time
import uuid

try:
    import libtmux
    import webob
except ImportError:
    print('Please install libtmux and webob')
    print('$ pip install libtmux webob')
    exit(1)


hosts_map = {
    "ha.prod.publik-hds.entrouvert.pf.internet.wf": "aquaray-hds-prod-node1",
    "ha.preprod.publik-hds.entrouvert.pf.internet.wf": "aquaray-hds-test-node1",
}


def process_args(url, service):
    req = webob.Request.blank(url.split('#')[0].split('?')[0])
    tenant = req.host.split(':')[0]
    code = None
    if req.path.startswith('/backoffice/'):
        service = 'wcs'
    if tenant.startswith(('agenda', 'chrono')):
        service = 'chrono'
    elif tenant.startswith(('connexion', 'authentic')):
        service = 'authentic-multitenant'
        code = [
            'from datetime import datetime, timedelta, timezone',
            'from authentic2.apps.journal.models import Event, EventType',
            ('\nEvent.objects.filter('
             'type__name="user.login",'
             'user__username__endswith="entrouvert.com",'
             'timestamp__gte=datetime.now(tz=timezone.utc)-timedelta(days=1)'
             ').values("timestamp", "user__username").count()'
             )
        ]
    elif tenant.startswith('passerelle'):
        service = 'passerelle'
    elif tenant.startswith('hobo'):
        service = 'hobo'
    elif tenant.startswith('paiement'):
        service = 'lingo'
    elif service == 'combo' or tenant.startswith(('portail', 'agents', 'combo')):
        service = 'combo'
        code = ['from combo.data.models import Page, CellBase']
        if req.path.startswith('/manage/pages/'):
            try:
                page_id = req.path.split('/')[3]
            except IndexError:
                pass
            else:
                code.extend(
                    [
                        f'page = Page.objects.get(id={page_id})',
                        'page',
                    ]
                )
    elif service == 'wcs':
        klass = None
        if req.path.startswith('/backoffice/management/'):
            klass = 'FormDef'
        elif req.path.startswith('/backoffice/data/'):
            klass = 'CardDef'
        if klass:
            try:
                slug, internal_id, *_ = req.path.split('/')[3:]
            except ValueError:
                pass
            else:
                code = [
                    f'from wcs.{klass.lower()} import {klass}',
                    f'{klass.lower()} = {klass}.get_by_slug("{slug}")',
                    f'{klass.lower()[:4]}data = {klass.lower()}.data_class().get_by_id("{internal_id}")',
                    f'{klass.lower()[:4]}data',
                ]
    return service, tenant, code


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument(
        '-s',
        '--service',
        default='wcs',
        help='Service name: combo/chrono/... in case its not obvious (default: wcs)',
    )
    parser.add_argument('-t', '--ssh-target', default=None, help='IP or name of the target server')
    parser.add_argument('-c', '--command', default='shell', choices=['shell', 'dbshell', 'grep'])
    parser.add_argument('url', metavar='URL', help='URL to use to determine the server/service')
    parser.add_argument('pattern', metavar='PATTERN', help='patterns to grep', nargs='*')
    parser.add_argument('--debug', default=False, action='store_true', help='debug mode')
    args = parser.parse_args()

    logging.basicConfig(
        level=logging.DEBUG if args.debug else logging.INFO, format='[%(levelname)s] %(message)s'
    )
    logging.getLogger('libtmux').setLevel(logging.WARNING)

    code = None
    colors = 'black', 'green'

    if not args.url:
        args.url = f'https://{args.service}.dev.publik.love'

    service, tenant, code = process_args(args.url, args.service)
    if service == 'authentic-multitenant':
        cmd = 'authentic2-multitenant-manage'
    else:
        cmd = f"{service}-manage"

    target = args.ssh_target or socket.gethostbyname(tenant)
    if target == '127.0.0.1':
        ssh_cmd = f'~/envs/publik-env-py3/bin/{cmd}'
    else:
        if not args.ssh_target:
            target = socket.gethostbyaddr(tenant)[0].replace('-output', '')
        target = hosts_map.get(target, target)
        ssh_cmd = f'ssh -t {target} sudo -u {service} {cmd}'
        if 'test' in tenant:
            colors = 'black', 'orange'
        else:
            colors = 'white', 'red'

    sock = f'eo-{str(uuid.uuid4())}.sock'

    logging.info(f'Connecting to {tenant} ({target})')
    ssh_cmd = f'{ssh_cmd} {args.command} -d {tenant}'
    if args.command == 'grep':
        if service != 'wcs':
            logging.error(f'grep command is only available for wcs service. Got {service}')
            return
        ssh_cmd += f' {" ".join(args.pattern)}'

    logging.debug('sock: %s', sock)
    logging.debug('ssh_cmd: %s', ssh_cmd)
    if code:
        logging.debug('code: %s', '; '.join(code))

    if args.debug:
        input('\nPress enter to continue')

    if args.command == 'grep':
        subprocess.run(ssh_cmd, shell=True)
        return

    # change terminal title
    print("\x1B]0;%s@%s\x07" % (service, tenant))

    server = libtmux.Server(socket_name=sock)
    session = server.new_session(
        session_name=service,
        window_name=tenant,
        window_command=ssh_cmd,
    )
    session.set_option('status-fg', colors[0])
    session.set_option('status-bg', colors[1])
    try:
        p = subprocess.Popen(['tmux', '-L', sock, 'attach-'])
        window = session.windows[0]
        pane = window.panes[0]
        if args.command == 'shell' and code:
            while True:
                stdout = pane.cmd('capture-pane', '-p').stdout
                time.sleep(0.1)
                if 'In [1]:' in stdout:
                    break
                if '(InteractiveConsole)' in stdout:
                    break
            pane.send_keys('; '.join(code))

    finally:
        p.wait()
        server.kill()


if __name__ == '__main__':
    main()
else:
    import pytest

    @pytest.mark.parametrize(
        'url,service,expected',
        [
            ('https://p-v.test.entrouvert.org', 'combo', 'combo'),
            ('https://p-v.test.entrouvert.org/backoffice/', 'combo', 'wcs'),
            ('https://p-v.test.entrouvert.org/backoffice/management/', 'combo', 'wcs'),
            ('https://portail-v.test.entrouvert.org/manage/pages/12/', 'combo', 'combo'),
            ('https://agendas.test.entrouvert.org', 'combo', 'chrono'),
            ('https://portail-v.test.entrouvert.org', 'wcs', 'combo'),
        ],
    )
    def test_process_args(url, service, expected):
        assert process_args(url, service)[0] == expected
