import logging

from enum import IntEnum
from flags import Flags
import auth_session
from wizart import prodtrack_api
from typing import List
from . import utils
import main_window
import os
from wizart.desktop.projects_registry import ProjectsAggregator

class ExecuteCommandError(Exception):
    pass


class CommandExecutionStatus(IntEnum):
    Success = 0
    Failed = 1


class Command(object):
    name = "base_command"
    label = "Execute Base Command"

    def __init__(self):
        super(Command, self).__init__()
        self.log = logging.getLogger("wizart_desktop")

    @classmethod
    def command_type_as_dict(self):
        raise NotImplementedError()

    def execute(self):
        raise NotImplementedError()


class ProdtrackEntityType(IntEnum):
    Unknown = 0
    Assets = 1
    Episodes = 2
    Scenes = 3
    Series = 4


class ProdtrackView(Flags):
    Unknown = 0
    TableView = 1 << 0
    DetailView = 1 << 1


class ProdtrackCommand(Command):
    view = ProdtrackView.Unknown
    entity_type = ProdtrackEntityType.Unknown

    def __init__(self, entity_ids):
        super(ProdtrackCommand, self).__init__()
        self.entity_ids = entity_ids  # List[int]
        self.session = auth_session.get_prodtrack_session()
        self.task_params = {}

    @classmethod
    def command_type_as_dict(cls):
        result = {}
        result["name"] = cls.name
        result["label"] = cls.label
        result["entity_type"] = int(cls.entity_type)
        result["view"] = cls.view
        return result

    @classmethod
    def get_command_type_by_name(cls, command_name):
        for clsobj in cls.__subclasses__():
            if clsobj.name == command_name:
                return clsobj

    def get_entities(self):
        if self.entity_type == ProdtrackEntityType.Scenes:
            return prodtrack_api.Scene.get_list(
                {"filter{id.in}": self.entity_ids, "filter{trash}": False, "include[]":["project.*"] },
                session=self.session,
            )
        elif self.entity_type == ProdtrackEntityType.Episodes:
            return prodtrack_api.Episode.get_list(
                {"filter{id.in}": self.entity_ids, "filter{trash}": False,  "include[]":["project.*"]},
                session=self.session,
            )    
        elif self.entity_type == ProdtrackEntityType.Series:
            return prodtrack_api.Series.get_list(
                {"filter{id.in}": self.entity_ids, "filter{trash}": False, "include[]":["project.*"]},
                session=self.session,
            )
        elif self.entity_type == ProdtrackEntityType.Assets:
            return prodtrack_api.Asset.get_list(
                {
                    "filter{id.in}": self.entity_ids,
                    "filter{trash}": False,
                    "include[]": ["type","project.*"],
                },
                session=self.session,
            )

    def visible_for(self, view):
        return (self.view & view) != 0

    def post_to_fs(self, url, json):
        fs_ctrl_session = auth_session.get_fs_ctrl_session()
        self.log.debug("%s" % json)
        response = fs_ctrl_session.post(url, json=json)
        if response.status_code != 200:
            format_args = dict(
                url=url,
                args=str(json),
                status=response.status_code,
                reason=response.reason,
            )
            raise ExecuteCommandError(
                "Failed request to url '{url}' with args: '{args}', status:{status}, reason: {reason} ".format(
                    **format_args
                )
            )
        self.task_params[response.json()['id']] = {'name':self.name, 'args':json}

class CreateSceneCommand(ProdtrackCommand):
    name = "create_scene"
    label = "Create Scene(s) On Disk"
    view = ProdtrackView.TableView | ProdtrackView.DetailView
    entity_type = ProdtrackEntityType.Scenes

    def __init__(self, entity_ids):
        super(CreateSceneCommand, self).__init__(entity_ids)

    def execute(self):
        self.log.info("%s %s" % (self.name, self.entity_ids))
        entities = self.get_entities()
        project = ProjectsAggregator.get_project_by_id(entities[0].data['project'])
        if not project:
            self.log.error("It is not possible to get prodtrack config for project")
            return CommandExecutionStatus.Failed
        prodtrack_proj_config = project.prodtrack_config
        for entity in entities:
            match = prodtrack_proj_config["scene_name_exp"].match(entity.data["name"])
            if not match:
                raise ExecuteCommandError(
                    "Mismatch scene name regexp '%s', for '%s'"
                    % (prodtrack_proj_config["scene_name_exp"], entity.data["name"])
                )
            command_arguments = {}
            command_arguments["episode"] = prodtrack_proj_config['episode_name_formatter'].format(name = match.groupdict()["episode"])
            command_arguments["name"] = prodtrack_proj_config['scene_name_formatter'].format(name = match.groupdict()["scene"])
            url = "http://fs_ctrl.burut.net/api/command/{project}/create_scene".format(project=project.name)
            if project.is_series:
                command_arguments["series"] = prodtrack_proj_config['series_name_formatter'].format(name = match.groupdict()["series"])
            self.post_to_fs(url, command_arguments)
        return CommandExecutionStatus.Success

class CreateEpisodeCommand(ProdtrackCommand):
    name = "create_episode"
    label = "Create Episode(s) On Disk"
    view = ProdtrackView.TableView | ProdtrackView.DetailView
    entity_type = ProdtrackEntityType.Episodes

    def __init__(self, entity_ids):
        super(CreateEpisodeCommand, self).__init__(entity_ids)

    def execute(self):
        self.log.info("%s %s" % (self.name, self.entity_ids))
        entities = self.get_entities()
        project = ProjectsAggregator.get_project_by_id(entities[0].data['project'])
        if not project:
            self.log.error("It is not possible to get prodtrack config for project")
            return CommandExecutionStatus.Failed
        prodtrack_proj_config = project.prodtrack_config
        for entity in entities:
            match = prodtrack_proj_config["episode_name_exp"].match(entity.data["name"])
            if not match:
                raise ExecuteCommandError(
                    "Mismatch episode name regexp '%s', for '%s'"
                    % (prodtrack_proj_config["episode_name_exp"], entity.data["name"])
                )
            command_arguments = {}
            command_arguments["name"] = prodtrack_proj_config['episode_name_formatter'].format(name = match.groupdict()["episode"])
            if project.is_series:
                command_arguments["series"] = prodtrack_proj_config['series_name_formatter'].format(name = match.groupdict()["series"])
            url = "http://fs_ctrl.burut.net/api/command/{project}/create_episode".format(project=project.name)
            self.post_to_fs(url, command_arguments)

        return CommandExecutionStatus.Success

class CreateSeriesCommand(ProdtrackCommand):
    name = "create_series"
    label = "Create Series On Disk"
    view = ProdtrackView.TableView | ProdtrackView.DetailView
    entity_type = ProdtrackEntityType.Series

    def __init__(self, entity_ids):
        super(CreateSeriesCommand, self).__init__(entity_ids)

    def execute(self):
        self.log.info("%s %s" % (self.name, self.entity_ids))
        entities = self.get_entities()
        project = ProjectsAggregator.get_project_by_id(entities[0].data['project'])
        if not project:
            self.log.error("It is not possible to get prodtrack config for project")
            return CommandExecutionStatus.Failed
        prodtrack_proj_config = project.prodtrack_config
        for entity in entities:
            match = prodtrack_proj_config["series_name_exp"].match(entity.data["name"])
            if not match:
                raise ExecuteCommandError(
                    "Mismatch series name regexp '%s', for '%s'"
                    % (prodtrack_proj_config["series_name_exp"], entity.data["name"])
                )
            command_arguments = {}
            command_arguments["name"] = prodtrack_proj_config['series_name_formatter'].format(name = match.groupdict()["series"])
            url = "http://fs_ctrl.burut.net/api/command/{project}/create_series".format(project=project.name)
            self.post_to_fs(url, command_arguments)
        return CommandExecutionStatus.Success

class CreateAssetCommand(ProdtrackCommand):
    name = "create_asset"
    label = "Create Asset(s) On Disk"
    view = ProdtrackView.TableView | ProdtrackView.DetailView
    entity_type = ProdtrackEntityType.Assets

    def __init__(self, entity_ids):
        super(CreateAssetCommand, self).__init__(entity_ids)

    def execute(self):
        self.log.info("%s %s" % (self.name, self.entity_ids))
        entities = self.get_entities()
        project = ProjectsAggregator.get_project_by_id(entities[0].data['project'])
        if not project:
            self.log.error("It is not possible to get prodtrack config for project")
            return CommandExecutionStatus.Failed
        asset_types = prodtrack_api.AssetType.get_list(session=self.session)

        def _get_asset_type_str(id):
            asset_type_map = dict(
                [
                    (asset_type.data["id"], asset_type.data["name"])
                    for asset_type in asset_types
                ]
            )
            asset_type_str = asset_type_map.get(id)
            if asset_type_str == "character":
                return "char"
            return asset_type_str

        for entity in entities:
            command_arguments = {}
            command_arguments["type"] = _get_asset_type_str(entity.data["type"])
            command_arguments["name"] = entity.data["name"]
            url = "http://fs_ctrl.burut.net/api/command/{project}/create_asset".format(project=project.name)
            self.post_to_fs(url, command_arguments)
        return CommandExecutionStatus.Success