import functools
import os
from dataclasses import dataclass
from enum import Enum
from typing import Any, Iterable, List, Optional, Type

import click

from ..config import run_first_time_setup
from ..enums import Bracket, Layout
from .help import get_help_string
from .parse import BRACKETS, LAYOUTS


# since we can't subclass click.Parameter, we have to do this
@dataclass
class Param:
    """Describes an ODHG CLI parameter."""
    # TODO: option_short, option_long ?

    # click.Parameter
    options: Iterable[str]
    default: Any = None
    is_flag: bool = False
    type: Type = None
    multiple: bool = False
    callback: Any = None

    # state
    enabled: bool = True
    
    # Help text
    argument_format: str = ""
    description: str = ""
    arguments: Optional[dict] = None
    argument_type: Optional[Enum] = None # this is quite hacky
    description_post: str = ""

    def __post_init__(self) -> None:
        if not self.argument_format:
            if self.is_flag:
                self.argument_format = "(flag)"
            elif type == int:
                self.argument_format = "NUMBER"
            elif type == str:
                self.argument_format = "TEXT"


def get_click_params() -> List[click.Option]:  
    return [
        click.Option(
            p.options,
            default=p.default,
            is_flag=p.is_flag,
            type=p.type,
            multiple=p.multiple,
            callback=p.callback
        )
        for p in PARAMS
        if p.enabled
    ]


def quiet() -> None:
    """Patches click.echo to send standard output to /dev/null."""
    devnull = open(os.devnull, "w", encoding="utf-8")
    click.echo = functools.partial(click.echo, file=devnull)


def setup(options: dict) -> dict:
    conf = run_first_time_setup()
    options.update(conf)
    return options


def help() -> None:
    click.echo(get_help_string())
    raise SystemExit    


# This is the alternative to stacking decorators on odhg.main()
# and it also makes it easier to gather documentation and behavior 
# of command parameters in one place
PARAMS = [
    Param(
        options=["-b", "--brackets"],
        multiple=True,
        argument_format=f"BRACKET (default: {Bracket.DEFAULT})",
        description="Which skill bracket to get winrates from.", 
        arguments=BRACKETS,
        argument_type=Bracket,
        description_post="Hero grids for multiple brackets can be generated "
                         "by specifying the -b option several times."
    ),
    Param(
        options=["-l", "--layout"],
        argument_format=f"LAYOUT (default: {Layout.DEFAULT})",
        description="Which Hero Grid layout to use.",
        arguments=LAYOUTS,
        argument_type=Layout
    ),
    Param(
        options=["-p", "--path"],
        argument_format="PATH",
        description="Specify absolute path of Dota 2 userdata/cfg directory.",
        description_post="(It's usually better to run --setup to configure this path.)",
    ),
    Param(
        options=["-a", "--ascending"],
        is_flag=True,
        default=True,
        description="Sort heroes by winrate in ascending order. (Default: descending).",
    ),
    Param(
        options=["-s", "--setup"],
        is_flag=True,
        description= "Runs first-time setup in order to create a persistent config.",
    ),
    Param(
        options=["-n", "--name"],
        type=str,
        argument_format="NAME",
        description="Sort heroes by winrate in an existing custom hero grid. "
        "This option is ONLY for sorting hand-made grids. "
        "Grids generated by ODHG do not require this option."
    ),
    Param(
        options=["--version"],
        is_flag=True,
        description= "Show program version.",
        enabled=False
    ),
    Param(
        options=["-h", "--help"],
        is_flag=True,
        description= "Show this message and exit.",
    ),
    Param(
        options=["--schedule"],
        is_flag=True,
        description= "Schedule ODHG to run periodically.\n"
                     "(crontab on UNIX-like systems, Task Scheduler on Windows)",
        enabled=False
    ),
    Param(
        options=["-q", "--quiet"],
        is_flag=True,
        argument_format="(flag)",
        description="Suppress all terminal output except errors.", # TODO: fix phrasing
    ),
        Param(
        options=["-N", "--nooverwrite"],
        is_flag=True,
        description="Disables overwriting of existing hero grids.",
        enabled=False
    ),
]
