"""Helper for listing upcoming (and past) projectum milestones.

   Projecta are small bite-sized project quanta that typically will result in
   one manuscript.
"""
import datetime as dt
import dateutil.parser as date_parser

from regolith.helpers.basehelper import SoutHelperBase
from regolith.fsclient import _id_key
from regolith.schemas import (PROJECTUM_ACTIVE_STATI, PROJECTUM_PAUSED_STATI,
   PROJECTUM_CANCELLED_STATI, PROJECTUM_FINISHED_STATI)
from regolith.tools import (
    all_docs_from_collection,
    get_pi_id,
    key_value_pair_filter,
    collection_str
)
from gooey import GooeyParser

TARGET_COLL = "projecta"
HELPER_TARGET = "l_projecta"
INACTIVE_STATI = PROJECTUM_PAUSED_STATI + PROJECTUM_CANCELLED_STATI

def subparser(subpi):
    date_kwargs = {}
    int_kwargs = {}
    if isinstance(subpi, GooeyParser):
        date_kwargs['widget'] = 'DateChooser'
        int_kwargs['widget'] = 'IntegerField'

    subpi.add_argument("-l", "--lead",
                       help="Filter milestones for this project lead, "
                            "specified by id, e.g., sbillinge")
    subpi.add_argument("-p", "--person",
                       help="Filter milestones for this person whether lead or "
                            "not, specified by id, e.g., sbillinge")
    subpi.add_argument("-g", "--grant",
                       help="Filter projecta by a grant ID")
    subpi.add_argument("-c", "--current", action="store_true",
                       help=f"Lists only active projecta")
    subpi.add_argument("-v", "--verbose", action="store_true",
                       help="increase verbosity of output")
    subpi.add_argument("--grp_by_lead", action='store_true',
                       help="Lists all projecta by their lead")
    subpi.add_argument("-o", "--orphans", action="store_true",
                       help="Find all orphans: prums that are assigned to 'tbd' or have a "
                            "non active person as lead")
    subpi.add_argument("--all", action="store_true",
                       help=f"Lists all projecta including those with statuses "
                            f"in {*INACTIVE_STATI,} that are excluded by default")
    subpi.add_argument("-e", "--ended", action="store_true",
                       help="Lists projecta that have ended. Use the -r flag to specify "
                            "how many days prior to today to search over for finished"
                            "prums (default is 7 days). "
                            "You may also use -d to change date used to search back"
                            "from (mostly used for testing)",)
    subpi.add_argument("-r", "--range",
                       help="date range back from DATE, in days, to search over "
                            "for finished prums."
                            "If no range is specified, search range will be 7 days",
                       )
    subpi.add_argument("-f", "--filter", nargs="+",
                       help="Search this collection by giving key element pairs")
    subpi.add_argument("-k", "--keys", nargs="+",
                       help="Specify what keys to return values from when running --filter. "
                            "If no argument is given the default is just the id.")
    subpi.add_argument("-d", "--date",
                       help="projecta with end_date within RANGE before this date will be listed. "
                            "The default is today. Some projecta don't have an end date and won't appear in a search. "
                            "mostly used for testing.",
                       **date_kwargs)
    return subpi


class ProjectaListerHelper(SoutHelperBase):
    """Helper for listing upcoming (and past) projectum milestones.

       Projecta are small bite-sized project quanta that typically will result in
       one manuscript.
    """
    # btype must be the same as helper target in helper.py
    btype = HELPER_TARGET
    needed_dbs = [f'{TARGET_COLL}', "people"]

    def construct_global_ctx(self):
        """Constructs the global context"""
        super().construct_global_ctx()
        gtx = self.gtx
        rc = self.rc
        if "groups" in self.needed_dbs:
            rc.pi_id = get_pi_id(rc)
        rc.coll = f"{TARGET_COLL}"
        colls = [
            sorted(
                all_docs_from_collection(rc.client, collname), key=_id_key
            )
            for collname in self.needed_dbs
        ]
        for db, coll in zip(self.needed_dbs, colls):
            gtx[db] = coll
        gtx["all_docs_from_collection"] = all_docs_from_collection
        gtx["float"] = float
        gtx["str"] = str
        gtx["zip"] = zip

    def sout(self):
        rc = self.rc
        if rc.filter:
            collection = key_value_pair_filter(self.gtx["projecta"], rc.filter)
        else:
            collection = self.gtx["projecta"]

        if (not rc.lead) and (not rc.person) and (not rc.ended) and (
        not rc.grant) and (not rc.verbose) and (not rc.grp_by_lead) and (
        not rc.filter) and (not rc.current) and (not rc.all) and (
        not rc.orphans):
            return
        if rc.date:
            now = date_parser.parse(rc.date).date()
        else:
            now = dt.date.today()
        if not rc.range:
            rc.range = 7
        since_date = now - dt.timedelta(days=int(rc.range))

        projecta, end_projecta, error_projecta = [], [], []
        grouped_projecta = {}
        if rc.grant:
            collection = [prum for prum in collection if
                          rc.grant in prum.get('grants', [])]
        if rc.orphans:
            if rc.person or rc.lead:
                raise RuntimeError(
                    f"you cannot specify lead or person with orphan")
            dead_parents = [person.get("_id") for person in self.gtx["people"]
                             if not person.get("active")]
            collection = [prum for prum in collection
                          if prum.get('lead') in dead_parents
                          and prum.get('status') in PROJECTUM_ACTIVE_STATI + PROJECTUM_PAUSED_STATI
                          or prum.get('lead').lower() == 'tbd']
        if rc.lead:
            if rc.person or rc.orphans:
                raise RuntimeError(
                    f"please specify either lead or person, not both")
            collection = [prum for prum in collection if
                          prum.get('lead') == rc.lead]
        if rc.person:
            if rc.orphans:
                raise RuntimeError(
                    f"please specify either lead or person, not both")
            if isinstance(rc.person, str):
                rc.person = [rc.person]
            collection = [prum for prum in collection
                          if prum.get('lead') in rc.person
                          or bool(
                    set(prum.get('group_members', [])).intersection(
                        set(rc.person)))]
        if rc.current:
            collection = [prum for prum in collection if
                          prum.get('status') in PROJECTUM_ACTIVE_STATI]
        if not rc.all:
            collection = [prum for prum in collection if
                          prum.get('status') not in INACTIVE_STATI
                          ]

        for projectum in collection:
            if rc.ended:
                if projectum.get('status') not in PROJECTUM_ACTIVE_STATI:
                    if projectum.get('status') in INACTIVE_STATI:
                        continue
                    elif projectum.get('status') not in PROJECTUM_FINISHED_STATI \
                            or not isinstance(projectum.get('end_date'),
                                              dt.date):
                        error_projecta.append(projectum)
                    else:
                        end_date = projectum.get('end_date')
                        if isinstance(end_date, str):
                            end_date = date_parser.parse(
                                end_date).date()
                        if since_date <= end_date <= now:
                            end_projecta.append(projectum)
                if end_projecta != []:
                    projecta = end_projecta
                continue
            projecta.append(projectum)

        if rc.verbose:
            for p in projecta:
                grants = None
                if p.get('grants'):
                    if isinstance(p.get('grants'), list):
                        grants = ' ,'.join(p.get('grants'))
                    else:
                        grants = p.get('grants')
                print(p.get('_id'))
                print(
                    f"    status: {p.get('status')}, begin_date: {p.get('begin_date')}, due_date: {p.get('due_date')}, end_date: {p.get('end_date')}, grant: {grants}")
                print(f"    description: {p.get('description')}")
                print("    team:")
                print(f"        lead: {p.get('lead')}")
                grp_members = None
                if p.get('group_members'):
                    grp_members = ', '.join(p.get('group_members'))
                collaborators = None
                if p.get('collaborators'):
                    collaborators = ', '.join(p.get('collaborators'))
                print(f"        group_members: {grp_members}")
                print(f"        collaborators: {collaborators}")
            return

        if rc.grp_by_lead:
            for p in projecta:
                if p.get('lead') not in grouped_projecta:
                    grouped_projecta[p.get('lead')] = [p.get('_id')]
                else:
                    grouped_projecta[p.get('lead')].append(p.get('_id'))
            for key, values in grouped_projecta.items():
                print(f"{key}:")
                for v in values:
                    print(f"    {v}")
            return

        projecta.sort(key=lambda prum: prum.get("_id"))
        if rc.keys:
            results = (collection_str(projecta, rc.keys))
            print(results, end="")
            return

        if end_projecta != []:
            if now == dt.date.today() and rc.range == 7:
                print("\nProjecta finished this past week! o(*^v^*)o")
            else:
                print(
                    f"\nProjecta finished within the {rc.range} days leading up to {now}")
        elif end_projecta == [] and rc.ended:
            if now == dt.date.today() and rc.range == 7:
                print("\nNo projecta finished this week")
            else:
                print(
                    f"\nNo projecta finished within the {rc.range} days leading up to {now}")

        for i in projecta:
            print(i.get("_id"))

        if error_projecta:
            print(
                "\nWARNING: These projecta have an issue with the end date and/or status, "
                "please run f_prum to set status to finished and add an end date")
            for i in error_projecta:
                print(i.get("_id"))
