import os

# everything this module links to sits beside it, so anchor the paths on this file rather
# than on the working directory: pytest imports it from tests/, and a cwd-relative target
# made the links point into tests/
PACKAGE_DIR = os.path.dirname(os.path.abspath(__file__))

def get_log_path(client):
    """Absolute path of the log file the shift schedule client writes

    Mirrors cm_msg_get_logfile in midas/src/midas.cxx: midas keeps its logs in
    /Logger/Message dir, falling back to /Logger/Data dir and then to the experiment
    directory, and reads a relative value as relative to the experiment directory. Writing
    the log there is what puts it in front of the operator - mhttpd builds its list of
    message facilities by globbing *.log in that same directory.

    Args:
        client (midas.client.client): midas client object

    Returns:
        str: path to write shiftschedule.log at
    """

    exptdir = os.path.dirname(os.environ['MIDAS_EXPTAB'])

    # midas takes a blank string as "not set", so an empty value has to fall through to the
    # next key the same way a missing one does
    logdir = ''
    for key in ('/Logger/Message dir', '/Logger/Data dir'):
        try:
            logdir = client.odb_get(key)
        except KeyError:
            continue

        if logdir:
            break

    # join drops exptdir when logdir is already absolute, which is the rule midas applies to
    # these keys. a blank logdir leaves the experiment directory, the last fallback
    return os.path.join(exptdir, logdir, 'shiftschedule.log')

def setup_odb(client, array_len=10):
    """Setup the ODB with default values

    Args:
        client (midas.client.client): midas client object
        array_len (int): length of the arrays to construct
    """

    # setup shiftsetup with defaults
    path = '/Shifts/ShiftSetup'

    # make name array
    if not client.odb_exists(f'{path}/name'):
        client.odb_set(f'{path}/name',
                       contents=['OWL<br>23:00-07:30',
                                 'DAY<br>07:00-15:30',
                                 'EVE<br>15:00-23:30']+['']*(array_len-3),
                       create_if_needed=True)
        print(f'Setup ODB path {path}/name')

    if not client.odb_exists(f'{path}/shiftids'):
        client.odb_set(f'{path}/shiftids',
                       contents=[1, 2, 3]+[0]*(array_len-3),
                       create_if_needed=True)
        print(f'Setup ODB path {path}/shiftids')

    # make offset arrays
    if not client.odb_exists(f'{path}/start_offset_mins'):
        client.odb_set(f'{path}/start_offset_mins',
                       contents=[-60, 420, 900]+[0]*(array_len-3),
                       create_if_needed=True)
        print(f'Setup ODB path {path}/start_offset_mins')

    if not client.odb_exists(f'{path}/stop_offset_mins'):
        client.odb_set(f'{path}/stop_offset_mins',
                       contents=[450, 930, 1410]+[0]*(array_len-3),
                       create_if_needed=True)
        print(f'Setup ODB path {path}/stop_offset_mins')

    # roles for shifters
    if not client.odb_exists(f'{path}/roles'):
        client.odb_set(f'{path}/roles',
                       contents=['Exp', 'Cryo'] + ['']*(array_len-2),
                       create_if_needed=True)
        print(f'Setup ODB path {path}/roles')

    # set notes
    msgs = ["Empty names and/or ids will cause shift to be omitted from the schedule.",
            "Offsets denote start/stop times as an offset from 00:00 on the day-of in minutes, to allow shifts to span multiple days.",
            "shiftids are unique ints to id each shift in the database. 0 < shiftid < 1000",
            ]
    for i, msg in enumerate(msgs):
        client.odb_set(f'{path}/note{i}',
                       contents=msg,
                       create_if_needed=True)
        print(f'Setup ODB path {path}/note{i}')

    # add some variables
    path = '/Shifts/Variables'

    # currently on shift
    if not client.odb_exists(f'{path}/onshift_names'):
        client.odb_set(f'{path}/onshift_names',
                       contents=[""]*array_len,
                       create_if_needed=True)
        print(f'Setup ODB path {path}/onshift_names')

    # shiftid of on shift shifters
    if not client.odb_exists(f'{path}/onshift_shiftid'):
        client.odb_set(f'{path}/onshift_shiftid',
                       contents=[0]*array_len,
                       create_if_needed=True)
        print(f'Setup ODB path {path}/onshift_shiftid')

    # display string of who is on shift, for the web page (the array above renders
    # with its empty padding when bound directly to a modbvalue span)
    if not client.odb_exists(f'{path}/onshift_names_str'):
        client.odb_set(f'{path}/onshift_names_str',
                       contents='nobody',
                       create_if_needed=True)
        print(f'Setup ODB path {path}/onshift_names_str')

    # stateid tracking history
    if not client.odb_exists(f'{path}/stateid'):
        client.odb_set(f'{path}/stateid',
                       contents=0,
                       create_if_needed=True)
        print(f'Setup ODB path {path}/stateid')

    # add some variables
    path = '/Shifts/Settings'

    # set flag for assign only available
    if not client.odb_exists(f'{path}/assign_only_available'):
        client.odb_set(f'{path}/assign_only_available',
                       contents=False,
                       create_if_needed=True)
        print(f'Setup ODB path {path}/assign_only_available')

    # set database type
    if not client.odb_exists(f'{path}/database'):
        client.odb_set(f'{path}/database',
                       contents='csv',
                       create_if_needed=True)
        print(f'Setup ODB path {path}/database')

    # set database options
    if not client.odb_exists(f'{path}/Options database'):
        client.odb_set(f'{path}/Options database',
                       contents=['csv', 'parquet'],
                       create_if_needed=True)
        print(f'Setup ODB path {path}/Options database')

    # flag to start on a sunday
    if not client.odb_exists(f'{path}/start_on_sunday'):
        client.odb_set(f'{path}/start_on_sunday',
                       contents=False,
                       create_if_needed=True)
        print(f'Setup ODB path {path}/start_on_sunday')

    # prevent shifts within x hours of previous shift
    if not client.odb_exists(f'{path}/min_hours_between_shifts'):
        client.odb_set(f'{path}/min_hours_between_shifts',
                       contents=0.0,
                       create_if_needed=True)
        print(f'Setup ODB path {path}/min_hours_between_shifts')

    # add custom page - get existing pages, if any
    try:
        custom_pages = client.odb_get('/Custom', recurse_dir=False)
    except KeyError:
        custom_pages = {}

    # check the values, not the key names, since the key sets the menu label and may
    # have been renamed by the user. subdirectories come back as dicts - skip those
    linked = [key for key, value in custom_pages.items()
              if isinstance(value, str)
              and os.path.basename(value) == 'shiftschedule.html']

    if linked:
        print(f'Custom page already linked as /Custom/{linked[0]}, skipping')
    else:
        client.odb_set(f'/Custom/ShiftSchedule',
                       contents='shiftschedule.html',
                       create_if_needed=True)
        print(f'Setup ODB path /Custom/ShiftSchedule')

    # make symlinks for html and dir and log
    try:
        webpath = client.odb_get('/Custom/Path')
    
    # path does not exist - add to default path
    except KeyError:
        webpath = os.path.join(os.environ['MIDASSYS'], 'resources')
        client.odb_set(f'Custom/Path',
                       contents=webpath,
                       create_if_needed=True)
        
    target = os.path.join(PACKAGE_DIR, 'shiftschedule.html')
    path = os.path.join(webpath, 'shiftschedule.html')
    if not os.path.lexists(path):
        os.symlink(target, path)
        print(f'Created symbolic link {path} -> {target}')
        
    path = os.path.join(webpath, 'shiftschedule')
    if not os.path.lexists(path):
        os.symlink(PACKAGE_DIR, path)
        print(f'Created symbolic link {path} -> {PACKAGE_DIR}')

    # the log used to live in the package with a symlink pointing at it. it is written to the
    # log directory directly now, so drop the links an earlier setup left behind - one sitting
    # at the new path would send the client's writes straight back into the package. only ever
    # remove a link, never a real log file
    for path in {get_log_path(client),
                 os.path.join(os.path.dirname(os.environ['MIDAS_EXPTAB']), 'shiftschedule.log')}:
        if os.path.islink(path):
            print(f'Removed obsolete symbolic link {path} -> {os.readlink(path)}')
            os.remove(path)

    # make data directory for databases
    path = os.path.join(webpath, 'shiftschedule', 'data')
    os.makedirs(path, exist_ok=True)
    print(f"Initialized {path} directory for databases")

    # setup program
    client.odb_set('/Programs/shiftschedule/Required', True)
    client.odb_set('/Programs/shiftschedule/Start command', 
                   os.path.join(PACKAGE_DIR, 'start_shiftschedule.bash'))
    print(f"Setup required program shiftschedule")
