"""
プロジェクトのユーザを表示する。
"""
import argparse
import logging
from typing import Any, Dict, List, Optional

import annofabapi
from annofabapi.models import Task

import annofabcli
from annofabcli import AnnofabApiFacade
from annofabcli.common.cli import AbstractCommandLineInterface, ArgumentParser, build_annofabapi_resource_and_login
from annofabcli.common.enums import FormatArgument
from annofabcli.common.visualize import AddProps

logger = logging.getLogger(__name__)


class ListTasksMain:
    def __init__(self, service: annofabapi.Resource, project_id: str):
        self.service = service
        self.facade = AnnofabApiFacade(service)
        self.project_id = project_id
        self.visualize = AddProps(self.service, project_id)

    @annofabcli.utils.allow_404_error
    def get_task(self, project_id: str, task_id: str) -> Task:
        task, _ = self.service.api.get_task(project_id, task_id)
        return task

    def get_task_list_from_task_id(self, project_id: str, task_id_list: List[str]) -> List[Task]:
        task_list = []
        logger.debug(f"{len(task_id_list)}件のタスクを取得します。")
        for index, task_id in enumerate(task_id_list):
            if (index + 1) % 100 == 0:
                logger.debug(f"{index+1} 件のタスクを取得します。")

            task = self.get_task(project_id, task_id)
            if task is not None:
                task_list.append(self.visualize.add_properties_to_task(task))
            else:
                logger.warning(f"タスク '{task_id}' は見つかりませんでした。")

        return task_list

    def _modify_task_query(self, project_id: str, task_query: Dict[str, Any]) -> Dict[str, Any]:
        """
        タスク検索クエリを修正する。
        ``user_id`` から ``account_id`` に変換する。
        ``previous_user_id`` から ``previcous_account_id`` に変換する。
        ``page`` , ``limit``を削除」する

        Args:
            task_query: タスク検索クエリ（変更される）

        Returns:
            修正したタスク検索クエリ

        """

        def remove_key(arg_key: str):
            if arg_key in task_query:
                logger.info(f"タスク検索クエリから、`{arg_key}`　キーを削除しました。")
                task_query.pop(arg_key)

        remove_key("page")
        remove_key("limit")

        if "user_id" in task_query:
            user_id = task_query["user_id"]
            account_id = self.facade.get_account_id_from_user_id(project_id, user_id)
            if account_id is not None:
                task_query["account_id"] = account_id
            else:
                logger.warning(f"タスク検索クエリに含まれている user_id: {user_id} のユーザが見つかりませんでした。")

        if "previous_user_id" in task_query:
            previous_user_id = task_query["previous_user_id"]
            previous_account_id = self.facade.get_account_id_from_user_id(project_id, previous_user_id)
            if previous_account_id is not None:
                task_query["previous_account_id"] = previous_account_id
            else:
                logger.warning(f"タスク検索クエリに含まれている previous_user_id: {previous_user_id} のユーザが見つかりませんでした。")

        return task_query

    def get_task_list_with_api(
        self, project_id: str, task_query: Optional[Dict[str, Any]] = None, user_id_list: Optional[List[str]] = None
    ) -> List[Task]:
        """
        タスク一覧を取得する。

        Args:
            project_id:
            task_id_list:

        Returns:
            対象の検査コメント一覧
        """
        if task_query is not None:
            task_query = self._modify_task_query(project_id, task_query)
        else:
            task_query = {}

        if user_id_list is None:
            logger.debug(f"task_query: {task_query}")
            tasks = self.service.wrapper.get_all_tasks(project_id, query_params=task_query)
            if len(tasks) == 10000:
                logger.warning("タスク一覧は10,000件で打ち切られている可能性があります。")

        else:
            tasks = []
            for user_id in user_id_list:
                task_query["user_id"] = user_id
                task_query = self._modify_task_query(project_id, task_query)
                logger.debug(f"task_query: {task_query}")
                sub_tasks = self.service.wrapper.get_all_tasks(project_id, query_params=task_query)
                if len(sub_tasks) == 10000:
                    logger.warning(f"user_id={user_id}で絞り込んだタスク一覧は10,000件で打ち切られている可能性があります。")
                tasks.extend(sub_tasks)

        return [self.visualize.add_properties_to_task(e) for e in tasks]

    def get_task_list(
        self,
        project_id: str,
        task_id_list: Optional[List[str]] = None,
        task_query: Optional[Dict[str, Any]] = None,
        user_id_list: Optional[List[str]] = None,
    ) -> List[Task]:
        """


        Args:
            project_id: 対象のproject_id
            task_id_list: 対象のタスクのtask_id
            task_query: タスク検索クエリ
            user_id_list:

        Returns:

        """
        if task_id_list is not None:
            task_list = self.get_task_list_from_task_id(project_id, task_id_list=task_id_list)
        else:
            task_list = self.get_task_list_with_api(project_id, task_query=task_query, user_id_list=user_id_list)

        return task_list


class ListTasks(AbstractCommandLineInterface):
    """
    タスクの一覧を表示する
    """

    def __init__(self, service: annofabapi.Resource, facade: AnnofabApiFacade, args: argparse.Namespace):
        super().__init__(service, facade, args)
        self.visualize = AddProps(self.service, args.project_id)

    def main(self):
        args = self.args

        task_id_list = annofabcli.common.cli.get_list_from_args(args.task_id)
        if len(task_id_list) == 0:
            task_id_list = None

        user_id_list = annofabcli.common.cli.get_list_from_args(args.user_id)
        if len(user_id_list) == 0:
            user_id_list = None

        task_query = annofabcli.common.cli.get_json_from_args(args.task_query)

        project_id = args.project_id
        super().validate_project(project_id, project_member_roles=None)

        main_obj = ListTasksMain(self.service, project_id=project_id)
        task_list = main_obj.get_task_list(
            project_id=project_id,
            task_id_list=task_id_list,
            task_query=task_query,
            user_id_list=user_id_list,
        )
        logger.debug(f"タスク一覧の件数: {len(task_list)}")

        if len(task_list) > 0:
            self.print_according_to_format(task_list)
        else:
            logger.info(f"タスク一覧の件数が0件のため、出力しません。")


def main(args):
    service = build_annofabapi_resource_and_login(args)
    facade = AnnofabApiFacade(service)
    ListTasks(service, facade, args).main()


def parse_args(parser: argparse.ArgumentParser):
    argument_parser = ArgumentParser(parser)

    argument_parser.add_project_id()

    query_group = parser.add_mutually_exclusive_group()

    # タスク検索クエリ
    query_group.add_argument(
        "-tq",
        "--task_query",
        type=str,
        help="タスクの検索クエリをJSON形式で指定します。指定しない場合は、すべてのタスクを取得します。"
        "`file://`を先頭に付けると、JSON形式のファイルを指定できます。"
        "クエリのフォーマットは、[getTasks API](https://annofab.com/docs/api/#operation/getTasks)のクエリパラメータと同じです。"
        "さらに追加で、`user_id`, `previous_user_id` キーも指定できます。"
        "ただし `page`, `limit`キーは指定できません。",
    )

    query_group.add_argument(
        "-t",
        "--task_id",
        type=str,
        nargs="+",
        help="対象のタスクのtask_idを指定します。`--task_query`引数とは同時に指定できません。" "`file://`を先頭に付けると、task_idの一覧が記載されたファイルを指定できます。",
    )

    parser.add_argument(
        "-u",
        "--user_id",
        type=str,
        nargs="+",
        help="絞り込み対象である担当者のuser_idを指定します。" "`file://`を先頭に付けると、task_idの一覧が記載されたファイルを指定できます。",
    )

    argument_parser.add_format(
        choices=[FormatArgument.CSV, FormatArgument.JSON, FormatArgument.PRETTY_JSON, FormatArgument.TASK_ID_LIST],
        default=FormatArgument.CSV,
    )
    argument_parser.add_output()
    argument_parser.add_csv_format()

    argument_parser.add_query()
    parser.set_defaults(subcommand_func=main)


def add_parser(subparsers: argparse._SubParsersAction):
    subcommand_name = "list"
    subcommand_help = "タスク一覧を出力します。"
    description = "タスク一覧を出力します。"

    parser = annofabcli.common.cli.add_parser(subparsers, subcommand_name, subcommand_help, description)
    parse_args(parser)
