#  pylint: disable=missing-module-docstring,invalid-name
#
# Copyright (c) 2008--2015 Red Hat, Inc.
#
# This software is licensed to you under the GNU General Public License,
# version 2 (GPLv2). There is NO WARRANTY for this software, express or
# implied, including the implied warranties of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
# along with this software; if not, see
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
#
# Red Hat trademarks are not licensed under GPLv2. No permission is
# granted to use or replicate Red Hat trademarks that are incorporated
# in this software or its documentation.
#
#

from spacewalk.common.rhnLog import log_debug
from spacewalk.server import rhnSQL


def schedule_action(
    action_type,
    action_name=None,
    delta_time=0,
    scheduler=None,
    org_id=None,
    prerequisite=None,
):
    action_id = rhnSQL.Sequence("rhn_event_id_seq").next()

    at = rhnSQL.Table("rhnActionType", "label")
    if not at.has_key(action_type):
        # pylint: disable-next=consider-using-f-string
        raise ValueError("Unknown action type %s" % action_type)

    params = {
        "action_id": action_id,
        "org_id": org_id,
        "action_type_id": at[action_type]["id"],
        "action_name": action_name,
        "delta": delta_time,
        "scheduler": scheduler,
        "prerequisite": prerequisite,
    }

    h = rhnSQL.prepare(
        """
        insert into rhnAction
               (id, org_id, action_type, name, scheduler, earliest_action, prerequisite)
        values (:action_id, :org_id, :action_type_id, :action_name, :scheduler,
                current_timestamp + numtodsinterval(:delta, 'second'), :prerequisite)
    """
    )
    h.execute(**params)

    return action_id


def schedule_server_action(
    server_id,
    action_type,
    action_name=None,
    delta_time=0,
    scheduler=None,
    org_id=None,
    prerequisite=None,
):
    if not org_id:
        h = rhnSQL.prepare("select org_id from rhnServer where id = :id")
        h.execute(id=server_id)
        row = h.fetchone_dict()
        if not row:
            # pylint: disable-next=consider-using-f-string
            raise ValueError("Invalid server id %s" % server_id)
        org_id = row["org_id"]

    action_id = schedule_action(
        action_type,
        action_name,
        delta_time=delta_time,
        scheduler=scheduler,
        org_id=org_id,
        prerequisite=prerequisite,
    )

    # Insert an action as Queued
    h = rhnSQL.prepare(
        """
        insert into rhnServerAction (server_id, action_id, status)
        values (:server_id, :action_id, 0)
    """
    )
    h.execute(server_id=server_id, action_id=action_id)

    return action_id


def update_server_action(
    server_id, action_id, status, result_code=None, result_message=""
):
    log_debug(4, server_id, action_id, status, result_code, result_message)
    h = rhnSQL.prepare(
        """
    update rhnServerAction
        set status = :status,
            result_code = :result_code,
            result_msg  = :result_message,
            completion_time = current_timestamp
    where action_id = :action_id
      and server_id = :server_id
    """
    )
    h.execute(
        action_id=action_id,
        server_id=server_id,
        status=status,
        result_code=result_code,
        result_message=result_message,
    )


_query_lookup_action_childs = rhnSQL.Statement(
    """
    select a.id
      from rhnAction a
      join rhnServerAction sa
        on sa.action_id = a.id
     where prerequisite = :action_id
       and sa.server_id = :server_id
"""
)

_query_lookup_action = rhnSQL.Statement(
    """
    select sa.action_id, sa.status
      from rhnServerAction sa
     where sa.server_id = :server_id
       and sa.action_id = :action_id
"""
)


def invalidate_action(server_id, action_id):
    log_debug(4, server_id, action_id)
    h = rhnSQL.prepare(_query_lookup_action)
    h_child = rhnSQL.prepare(_query_lookup_action_childs)
    return _invalidate_action_recursive(server_id, action_id, h, h_child)


def _invalidate_action_recursive(server_id, action_id, h, h_child):
    h_child.execute(server_id=server_id, action_id=action_id)
    a_ids = []
    # invalidate childs first
    while 1:
        row = h_child.fetchone_dict()
        if not row:
            break
        child_ids = _invalidate_action_recursive(server_id, row["id"], h, h_child)
        a_ids += child_ids
    h.execute(server_id=server_id, action_id=action_id)
    s_row = h.fetchone_dict()
    if s_row and s_row["status"] != 3:
        # not already failed
        c_action_id = s_row["action_id"]
        a_ids.append(c_action_id)
        update_server_action(
            server_id=server_id,
            action_id=c_action_id,
            status=3,
            result_code=-100,
            result_message="Prerequisite failed",
        )

    return a_ids


_query_schedule_server_packages_update_by_arch = rhnSQL.Statement(
    """
    insert into rhnActionPackage (id, action_id, name_id, package_arch_id, \
           parameter)
    values (sequence_nextval('rhn_act_p_id_seq'), :action_id, :name_id, :arch_id, \
           'upgrade')
"""
)


def schedule_server_packages_update_by_arch(
    server_id,
    package_arch_ids,
    org_id=None,
    prerequisite=None,
    action_name="Package update",
):
    action_id = schedule_server_action(
        server_id,
        action_type="packages.update",
        action_name=action_name,
        org_id=org_id,
        prerequisite=prerequisite,
    )
    h = rhnSQL.prepare(_query_schedule_server_packages_update_by_arch)

    for name_id, arch_id in package_arch_ids:
        h.execute(action_id=action_id, name_id=name_id, arch_id=arch_id)
    return action_id
