# Copyright (C) 2016 Google Inc.
# Licensed under http://www.apache.org/licenses/LICENSE-2.0 <see LICENSE file>

"""A module containing the workflow TaskGroup model."""


from sqlalchemy import or_

from ggrc import db
from ggrc.login import get_current_user
from ggrc.models.associationproxy import association_proxy
from ggrc.models.mixins import (
    Titled, Slugged, Described, Timeboxed, WithContact
)
from ggrc.models.reflection import AttributeInfo
from ggrc.models.reflection import PublishOnly
from ggrc.models import all_models
from ggrc_workflows.models.task_group_object import TaskGroupObject


class TaskGroup(
        WithContact, Timeboxed, Described, Titled, Slugged, db.Model):
  """Workflow TaskGroup model."""

  __tablename__ = 'task_groups'
  _title_uniqueness = False

  workflow_id = db.Column(
      db.Integer,
      db.ForeignKey('workflows.id', ondelete="CASCADE"),
      nullable=False,
  )
  lock_task_order = db.Column(db.Boolean(), nullable=True)

  task_group_objects = db.relationship(
      'TaskGroupObject', backref='task_group', cascade='all, delete-orphan')

  objects = association_proxy(
      'task_group_objects', 'object', 'TaskGroupObject')

  task_group_tasks = db.relationship(
      'TaskGroupTask', backref='task_group', cascade='all, delete-orphan')

  cycle_task_groups = db.relationship(
      'CycleTaskGroup', backref='task_group')

  sort_index = db.Column(
      db.String(length=250), default="", nullable=False)

  _publish_attrs = [
      'workflow',
      'task_group_objects',
      PublishOnly('objects'),
      'task_group_tasks',
      'lock_task_order',
      'sort_index',
      # Intentionally do not include `cycle_task_groups`
      # 'cycle_task_groups',
  ]

  _aliases = {
      "title": "Summary",
      "description": "Details",
      "contact": {
          "display_name": "Assignee",
          "mandatory": True,
          "filter_by": "_filter_by_contact",
      },
      "secondary_contact": None,
      "start_date": None,
      "end_date": None,
      "workflow": {
          "display_name": "Workflow",
          "mandatory": True,
          "filter_by": "_filter_by_workflow",
      },
      "task_group_objects": {
          "display_name": "Objects",
          "type": AttributeInfo.Type.SPECIAL_MAPPING,
          "filter_by": "_filter_by_objects",
      },
  }

  def copy(self, _other=None, **kwargs):
    columns = [
        'title', 'description', 'workflow', 'sort_index', 'modified_by',
        'context'
    ]

    if kwargs.get('clone_people', False) and getattr(self, "contact"):
      columns.append("contact")
    else:
      kwargs["contact"] = get_current_user()

    target = self.copy_into(_other, columns, **kwargs)

    if kwargs.get('clone_objects', False):
      self.copy_objects(target, **kwargs)

    if kwargs.get('clone_tasks', False):
      self.copy_tasks(target, **kwargs)

    return target

  def copy_objects(self, target, **kwargs):
    # pylint: disable=unused-argument
    for task_group_object in self.task_group_objects:
      target.task_group_objects.append(task_group_object.copy(
          task_group=target,
          context=target.context,
      ))

    return target

  def copy_tasks(self, target, **kwargs):
    for task_group_task in self.task_group_tasks:
      target.task_group_tasks.append(task_group_task.copy(
          None,
          task_group=target,
          context=target.context,
          clone_people=kwargs.get("clone_people", False),
      ))

    return target

  @classmethod
  def _filter_by_workflow(cls, predicate):
    from ggrc_workflows.models import Workflow
    return Workflow.query.filter(
        (Workflow.id == cls.workflow_id) &
        (predicate(Workflow.slug) | predicate(Workflow.title))
    ).exists()

  @classmethod
  def _filter_by_objects(cls, predicate):
    parts = []
    for model_name in all_models.__all__:
      model = getattr(all_models, model_name)
      query = getattr(model, "query", None)
      field = getattr(model, "slug", getattr(model, "email", None))
      if query is None or field is None or not hasattr(model, "id"):
        continue
      parts.append(query.filter(
          (TaskGroupObject.object_type == model_name) &
          (model.id == TaskGroupObject.object_id) &
          predicate(field)
      ).exists())
    return TaskGroupObject.query.filter(
        (TaskGroupObject.task_group_id == cls.id) &
        or_(*parts)
    ).exists()

[((35, 23, 36, 76), 'ggrc.db.relationship', 'db.relationship', (), '', False, 'from ggrc import db\n'), ((38, 12, 39, 56), 'ggrc.models.associationproxy.association_proxy', 'association_proxy', ({(39, 6, 39, 26): '"""task_group_objects"""', (39, 28, 39, 36): '"""object"""', (39, 38, 39, 55): '"""TaskGroupObject"""'}, {}), "('task_group_objects', 'object', 'TaskGroupObject')", False, 'from ggrc.models.associationproxy import association_proxy\n'), ((41, 21, 42, 74), 'ggrc.db.relationship', 'db.relationship', (), '', False, 'from ggrc import db\n'), ((44, 22, 45, 45), 'ggrc.db.relationship', 'db.relationship', (), '', False, 'from ggrc import db\n'), ((30, 6, 30, 55), 'ggrc.db.ForeignKey', 'db.ForeignKey', (), '', False, 'from ggrc import db\n'), ((33, 30, 33, 42), 'ggrc.db.Boolean', 'db.Boolean', ({}, {}), '()', False, 'from ggrc import db\n'), ((48, 6, 48, 27), 'ggrc.db.String', 'db.String', (), '', False, 'from ggrc import db\n'), ((53, 6, 53, 28), 'ggrc.models.reflection.PublishOnly', 'PublishOnly', ({(53, 18, 53, 27): '"""objects"""'}, {}), "('objects')", False, 'from ggrc.models.reflection import PublishOnly\n'), ((93, 26, 93, 44), 'ggrc.login.get_current_user', 'get_current_user', ({}, {}), '()', False, 'from ggrc.login import get_current_user\n'), ((150, 8, 150, 19), 'sqlalchemy.or_', 'or_', ({(150, 12, 150, 18): '*parts'}, {}), '(*parts)', False, 'from sqlalchemy import or_\n')]