# This file is part of Indico.
# Copyright (C) 2002 - 2021 CERN
#
# Indico is free software; you can redistribute it and/or
# modify it under the terms of the MIT License; see the
# LICENSE file for more details.

from flask import session

from indico.core import signals
from indico.core.db import db
from indico.modules.events.logs.models.entries import EventLogKind, EventLogRealm
from indico.modules.events.logs.util import make_diff_log
from indico.modules.events.models.events import EventType
from indico.modules.events.sessions import COORDINATOR_PRIV_SETTINGS, COORDINATOR_PRIV_TITLES, logger, session_settings
from indico.modules.events.sessions.models.blocks import SessionBlock
from indico.modules.events.sessions.models.sessions import Session
from indico.util.i18n import orig_string


def create_session(event, data):
    """
    Create a new session with the information passed in the `data` argument.
    """
    event_session = Session(event=event)
    event_session.populate_from_dict(data)
    db.session.flush()
    event.log(EventLogRealm.management, EventLogKind.positive, 'Sessions',
              f'Session "{event_session.title}" has been created', session.user,
              meta={'session_id': event_session.id})
    logger.info('Session %s created by %s', event_session, session.user)
    return event_session


def create_session_block(session_, data):
    block = SessionBlock(session=session_)
    block.populate_from_dict(data)
    db.session.flush()
    session_.event.log(EventLogRealm.management, EventLogKind.positive, 'Sessions',
                       'Session block "{}" for session "{}" has been created'
                       .format(block.title, session_.title), session.user,
                       meta={'session_block_id': block.id})
    logger.info('Session block %s created by %s', block, session.user)
    return block


def update_session(event_session, data):
    """Update a session based on the information in the `data`."""
    event_session.populate_from_dict(data)
    db.session.flush()
    signals.event.session_updated.send(event_session)
    event_session.event.log(EventLogRealm.management, EventLogKind.change, 'Sessions',
                            f'Session "{event_session.title}" has been updated', session.user,
                            meta={'session_id': event_session.id})
    logger.info('Session %s modified by %s', event_session, session.user)


def _delete_session_timetable_entries(event_session):
    for block in event_session.blocks:
        for contribution in block.contributions:
            if contribution.timetable_entry:
                db.session.delete(contribution.timetable_entry)
        if not block.timetable_entry:
            continue
        for child_block in block.timetable_entry.children:
            db.session.delete(child_block)
        db.session.delete(block.timetable_entry)


def delete_session(event_session):
    """Delete session from the event."""
    event_session.is_deleted = True
    for contribution in event_session.contributions[:]:
        contribution.session = None
    _delete_session_timetable_entries(event_session)
    signals.event.session_deleted.send(event_session)
    event_session.event.log(EventLogRealm.management, EventLogKind.negative, 'Sessions',
                            f'Session "{event_session.title}" has been deleted', session.user,
                            meta={'session_id': event_session.id})
    logger.info('Session %s deleted by %s', event_session, session.user)


def update_session_block(session_block, data):
    """Update a session block with data passed in the `data` argument."""
    from indico.modules.events.timetable.operations import update_timetable_entry

    start_dt = data.pop('start_dt', None)
    if start_dt is not None:
        session_block.timetable_entry.move(start_dt)
        update_timetable_entry(session_block.timetable_entry, {'start_dt': start_dt})
    session_block.populate_from_dict(data)
    db.session.flush()
    signals.event.session_block_updated.send(session_block)
    session_block.event.log(EventLogRealm.management, EventLogKind.change, 'Sessions',
                            f'Session block "{session_block.title}" has been updated', session.user,
                            meta={'session_block_id': session_block.id})
    logger.info('Session block %s modified by %s', session_block, session.user)


def delete_session_block(session_block):
    from indico.modules.events.contributions.operations import delete_contribution
    from indico.modules.events.timetable.operations import delete_timetable_entry
    session_ = session_block.session
    event = session_.event
    unschedule_contribs = session_.event.type_ == EventType.conference
    for contribution in session_block.contributions[:]:
        contribution.session_block = None
        if unschedule_contribs:
            delete_timetable_entry(contribution.timetable_entry, log=False)
        else:
            delete_contribution(contribution)
    for entry in session_block.timetable_entry.children[:]:
        delete_timetable_entry(entry, log=False)
    delete_timetable_entry(session_block.timetable_entry, log=False)
    signals.event.session_block_deleted.send(session_block)
    if session_block in session_.blocks:
        session_.blocks.remove(session_block)
    if not session_.blocks and session_.event.type != 'conference':
        delete_session(session_)
    db.session.flush()
    event.log(EventLogRealm.management, EventLogKind.negative, 'Sessions',
              f'Session block "{session_block.title}" has been deleted', session.user,
              meta={'session_block_id': session_block.id})
    logger.info('Session block %s deleted by %s', session_block, session.user)


def update_session_coordinator_privs(event, data):
    changes = {}
    for priv, enabled in data.items():
        setting = COORDINATOR_PRIV_SETTINGS[priv]
        if session_settings.get(event, setting) == enabled:
            continue
        session_settings.set(event, setting, enabled)
        changes[priv] = (not enabled, enabled)
    db.session.flush()
    logger.info('Session coordinator privs of event %r updated with %r by %r', event, data, session.user)
    if changes:
        log_fields = {priv: orig_string(title) for priv, title in COORDINATOR_PRIV_TITLES.items()}
        event.log(EventLogRealm.management, EventLogKind.change, 'Sessions', 'Coordinator privileges updated',
                  session.user, data={'Changes': make_diff_log(changes, log_fields)})
