from abc import ABCMeta, abstractmethod
from collections import Counter, defaultdict
from enum import Enum
from numbers import Number
from typing import Dict, Iterable, Optional, Union

from pydantic import BaseModel, Field

from logis.data_type import NumberType

TaskId = Union[str, int]
TaskPriority = Union[str, int]


class TaskStatus(Enum):
    """
    任务状态
    """

    NOT_STARTED = "not_started"
    STARTED = "started"
    PAUSED = "paused"
    FINISHED = "finished"
    CANCELLED = "cancelled"
    FAILED = "failed"


class ITask(metaclass=ABCMeta):
    """
    任务基础抽象类
    """

    @abstractmethod
    def get_task_id(self) -> TaskId:
        pass

    @abstractmethod
    def get_priority(self) -> TaskPriority:
        """
        任务优先级
        """
        pass

    @abstractmethod
    def update_status(self, status: TaskStatus):
        """
        更新任务状态
        """
        pass

    @abstractmethod
    def is_status_at(self, status: TaskStatus) -> bool:
        """
        检查任务状态是否为指定值
        """
        pass

    @property
    def finished(self) -> bool:
        return self.is_status_at(TaskStatus.FINISHED)


from logis.data_type import NumberUnit


class QuantifiedTask(NumberUnit, ITask):
    """
    可量化的任务
    """

    progress: Union[Number] = 0

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.pipeline_progress: Dict[str, Number] = Counter()

    def update_progress(
        self,
        progress: Union[NumberUnit, Number],
        delta: bool = False,
        pipeline_id: Optional[str] = None,
    ):
        """
        更新进度

        Args:

        """
        if isinstance(progress, NumberUnit):
            assert (
                progress.unit == self.unit
            ), f"进度单位{progress.unit}与任务单位{self.unit}不一致，请在调用处处理"
            progress = progress.quantity

        if pipeline_id is not None:
            if delta:
                self.pipeline_progress[pipeline_id] += progress
            else:
                self.pipeline_progress[pipeline_id] = progress
        else:
            if delta:
                self.progress += progress
            else:
                self.progress = progress


TaskLike = Union[ITask, TaskId]


class Task(BaseModel, ITask):
    """
    任务基础结构
    """

    id: Optional[TaskId] = None
    parent_id: Optional[TaskId] = None
    children_ids: Iterable[TaskId] = Field(default_factory=list)

    name: Optional[str] = None
    type: Optional[str] = None
    priority: Optional[TaskPriority] = None

    repeat: int = 1
    progress: Union[float, int] = 0
    tags: Iterable[str] = Field(default_factory=set)

    created_at: Optional[int] = None
    updated_at: Optional[int] = None
    started_at: Optional[int] = None
    finished_at: Optional[int] = None
    cancelled_at: Optional[int] = None

    status: Optional[TaskStatus] = None
    stage: Optional[str] = None

    remark: Optional[str] = None

    def get_task_id(self) -> TaskId:
        return self.id

    def get_priority(self) -> TaskPriority:
        return self.priority

    def update_status(self, status: TaskStatus):
        self.status = status

    def is_status_at(self, status: TaskStatus) -> bool:
        return self.status == status
