from contextlib import suppress
from enum import Enum, IntEnum, unique
from typing import Set, Union

import inflection


@unique
class SnapshotSoftware(Enum):
    """The software used to perform the Snapshot."""

    Workbench = 'Workbench'
    WorkbenchAndroid = 'WorkbenchAndroid'
    AndroidApp = 'AndroidApp'
    Web = 'Web'
    DesktopApp = 'DesktopApp'
    WorkbenchDesktop = 'WorkbenchDesktop'

    def __str__(self):
        return self.name


R_POSITIVE = 0, 10
R_NEGATIVE = -3, 5


@unique
class RatingRange(IntEnum):
    """
    The human translation to score range.

    You can compare them: ScoreRange.VERY_LOW < ScoreRange.LOW.
    There are four levels:

    1. Very low.
    2. Low.
    3. Medium.
    4. High.
    """

    VERY_LOW = 1
    LOW = 2
    MEDIUM = 3
    HIGH = 4

    @classmethod
    def from_score(cls, val: Union[int, float]) -> 'RatingRange':
        assert 0 <= val <= 10, 'Value is not a valid score.'

        if val <= cls.VERY_LOW:
            return cls.VERY_LOW
        elif val <= cls.LOW:
            return cls.LOW
        elif val <= cls.MEDIUM:
            return cls.MEDIUM
        else:
            return cls.HIGH

    def __str__(self):
        return inflection.humanize(self.name)

    def __format__(self, format_spec):
        return str(self)


@unique
class PriceSoftware(Enum):
    Ereuse = 'Ereuse'


@unique
class AppearanceRange(Enum):
    """Grades the imperfections that aesthetically affect the device, but not its usage."""

    Z = 'Z. The device is new'
    A = 'A. Is like new; without visual damage'
    B = 'B. Is in really good condition; small visual damage in difficult places to spot'
    C = 'C. Is in good condition; small visual damage in parts that are easy to spot, minor cosmetic blemishes on chassis'
    D = 'D. Is acceptable; visual damage in visible parts, major cosmetic blemishes on chassis, missing cosmetic parts'
    E = 'E. Is unacceptable; considerable visual damage, missing essential parts'

    def __str__(self):
        return self.name


@unique
class FunctionalityRange(Enum):
    """Grades the defects of a device that affect its usage."""

    A = 'A. All the buttons works perfectly, no screen/camera defects and chassis without usage issues'
    B = 'B. There is a button difficult to press or unstable it, a screen/camera defect or chassis problem'
    C = 'C.	Chassis defects or multiple buttons don\'t work; broken or unusable it, some screen/camera defect'
    D = 'D.	Chassis severity usage problems. All buttons, screen or camera don\'t work; broken or unusable it'

    def __str__(self):
        return self.name


@unique
class BatteryHealthRange(Enum):
    """Grade the battery health status, depending on self report Android system"""

    A = 'A. The battery health is very good'
    B = 'B. Battery health is good'
    C = 'C.	Battery health is overheat / over voltage status but can stand the minimum duration'
    D = 'D.	Battery health is bad; can’t stand the minimum duration time'
    E = 'E. Battery health is very bad; and status is dead; unusable or miss it'
    NONE = 'NA. Grade doesn’t exists'

    def __str__(self):
        return self.name


@unique
class BiosAccessRange(Enum):
    """How difficult it has been to set the bios to boot from the network."""

    A = 'A. If by pressing a key you could access a boot menu with the network boot'
    B = 'B. You had to get into the BIOS, and in less than 5 steps you could set the network boot'
    C = 'C. Like B, but with more than 5 steps'
    D = 'D. Like B or C, but you had to unlock the BIOS (i.e. by removing the battery)'
    E = 'E. The device could not be booted through the network.'

    def __str__(self):
        return self.name


@unique
class Orientation(Enum):
    Vertical = 'vertical'
    Horizontal = 'Horizontal'


@unique
class TestDataStorageLength(Enum):
    Short = 'Short'
    Extended = 'Extended'


@unique
class ImageSoftware(Enum):
    Photobox = 'Photobox'


@unique
class ImageMimeTypes(Enum):
    """Supported image Mimetypes for Devicehub."""

    jpg = 'image/jpeg'
    png = 'image/png'


BOX_RATE_5 = 1, 5
BOX_RATE_3 = 1, 3


# After looking at own databases


@unique
class RamInterface(Enum):
    """
    The interface or type of RAM.

    The more common type of RAM nowadays for RamModules is SDRAM.
    Note that we have not added all sub-types (please contact if
    you want them). See more here
    https://en.wikipedia.org/wiki/Category:SDRAM.

    Although SDRAM is the generic naming for any DDR we include it
    here for those cases where there is no more specific information.
    Please, try to always use DDRø-6 denominations.
    """

    SDRAM = 'SDRAM'
    DDR = 'DDR SDRAM'
    DDR2 = 'DDR2 SDRAM'
    DDR3 = 'DDR3 SDRAM'
    DDR4 = 'DDR4 SDRAM'
    DDR5 = 'DDR5 SDRAM'
    DDR6 = 'DDR6 SDRAM'
    LPDDR3 = 'LPDDR3'

    def __str__(self):
        return self.value


@unique
class RamFormat(Enum):
    DIMM = 'DIMM'
    SODIMM = 'SODIMM'

    def __str__(self):
        return self.value


@unique
class DataStorageInterface(Enum):
    ATA = 'ATA'
    USB = 'USB'
    PCI = 'PCI'
    NVME = 'NVME'

    def __str__(self):
        return self.value


@unique
class DisplayTech(Enum):
    CRT = 'Cathode ray tube (CRT)'
    TFT = 'Thin-film-transistor liquid-crystal (TFT)'
    LED = 'LED-backlit (LED)'
    PDP = 'Plasma display panel (Plasma)'
    LCD = 'Liquid-crystal display (any of TFT, LED, Blue Phase, IPS)'
    OLED = 'Organic light-emitting diode (OLED)'
    AMOLED = 'Organic light-emitting diode (AMOLED)'

    def __str__(self):
        return self.name


@unique
class ComputerChassis(Enum):
    """The chassis of a computer."""

    Tower = 'Tower'
    Docking = 'Docking'
    AllInOne = 'All in one'
    Microtower = 'Microtower'
    PizzaBox = 'Pizza box'
    Lunchbox = 'Lunchbox'
    Stick = 'Stick'
    Netbook = 'Netbook'
    Handheld = 'Handheld'
    Laptop = 'Laptop'
    Convertible = 'Convertible'
    Detachable = 'Detachable'
    Tablet = 'Tablet'
    Virtual = 'Non-physical device'

    def __str__(self):
        return inflection.humanize(inflection.underscore(self.value))


class ReceiverRole(Enum):
    """
    The role that the receiver takes in the reception;
    the meaning of the reception.
    """

    Intermediary = 'Generic user in the workflow of the device.'
    FinalUser = 'The user that will use the device.'
    CollectionPoint = 'A collection point.'
    RecyclingPoint = 'A recycling point.'
    Transporter = 'An user that ships the devices to another one.'


class PrinterTechnology(Enum):
    """Technology of the printer."""

    Toner = 'Toner / Laser'
    Inkjet = 'Liquid inkjet'
    SolidInk = 'Solid ink'
    Dye = 'Dye-sublimation'
    Thermal = 'Thermal'


@unique
class CameraFacing(Enum):
    Front = 'Front'
    Back = 'Back'


@unique
class BatteryHealth(Enum):
    """The battery health status as in Android."""

    Cold = 'Cold'
    Dead = 'Dead'
    Good = 'Good'
    Overheat = 'Overheat'
    OverVoltage = 'OverVoltage'
    UnspecifiedValue = 'UnspecifiedValue'


@unique
class BatteryTechnology(Enum):
    """The technology of the Battery. Adapted from
    https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-power
    adding ``Alkaline``.
    """

    LiIon = 'Lithium-ion'
    NiCd = 'Nickel-Cadmium'
    NiMH = 'Nickel-metal hydride'
    LiPoly = 'Lithium-ion Polymer'
    LiFe = 'LiFe'
    LiMn = 'Lithium Manganese'
    Al = 'Alkaline'


class Severity(IntEnum):
    """A flag evaluating the action execution. Ex. failed actions
    have the value `Severity.Error`. Devicehub uses 4 severity levels:

    * Info (Pass): default neutral severity. The action succeeded.
    * Notice: The action succeeded but it is raising awareness.
      Notices are not usually that important but something
      (good or bad) worth checking.
    * Warning: The action succeeded but there is something important
      to check negatively affecting the action.
    * Error (Fail): the action failed.

    Devicehub specially raises user awareness when an action
    has a Severity of ``Warning`` or greater.
    """

    Info = 0
    Notice = 1
    Warning = 2
    Error = 3

    def __str__(self):
        if self == self.Info:
            m = '✓'
        elif self == self.Notice:
            m = 'ℹ️'
        elif self == self.Warning:
            m = '⚠'
        else:
            m = '❌'
        return m

    def __format__(self, format_spec):
        return str(self)


class PhysicalErasureMethod(Enum):
    """Methods of physically erasing the data-storage, usually
    destroying the whole component.

    Certified data-storage destruction mean, as of `UNE-EN 15713
    <https://www.une.org/encuentra-tu-norma/busca-tu-norma/norma?c=N0044792>`_,
    reducing the material to a size making it undecipherable, illegible,
    and non able to be re-built.
    """

    Shred = 'Reduction of the data-storage to the required certified ' 'standard sizes.'
    Disintegration = (
        'Reduction of the data-storage to smaller sizes '
        'than the certified standard ones.'
    )

    def __str__(self):
        return self.name


class ErasureStandards(Enum):
    """Software erasure standards."""

    HMG_IS5 = 'British HMG Infosec Standard 5 (HMG IS5)'
    """`British HMG Infosec Standard 5 (HMG IS5) 
    <https://en.wikipedia.org/wiki/Infosec_Standard_5>`_.
    
    In order to follow this standard, an erasure must have the
    following steps:
    
    1. A first step writing zeroes to the data-storage units.
    2. A second step erasing with random data, verifying the erasure
       success in each hard-drive sector.
    
    And be an :class:`ereuse_devicehub.resources.action.models.EraseSectors`.
    """

    def __str__(self):
        return self.value

    @classmethod
    def from_data_storage(cls, erasure) -> Set['ErasureStandards']:
        """Returns a set of erasure standards."""
        from ereuse_devicehub.resources.action import models as actions

        standards = set()
        if isinstance(erasure, actions.EraseSectors):
            with suppress(ValueError):
                first_step, *other_steps = erasure.steps
                if isinstance(first_step, actions.StepZero) and all(
                    isinstance(step, actions.StepRandom) for step in other_steps
                ):
                    standards.add(cls.HMG_IS5)
        return standards


@unique
class TransferState(IntEnum):
    """State of transfer for a given Lot of devices."""

    """
    * Initial: No transfer action in place.
    * Initiated: The transfer action has been initiated by orginator.
    * Accepted: The  transfer action has been accepted by destinator.

    Devicehub specially raises user awareness when an action
    has a Severity of ``Warning`` or greater.
    """

    Initial = 0
    Initiated = 1
    Accepted = 2
    Completed = 3

    def __str__(self):
        return self.name


@unique
class SessionType(IntEnum):
    """
    Enumaration of types of sessions:

    * Internal: permanent Session for internal user. This is used in usb of WorkBench.
    * External: permanent Session for external users. This is used in usb of WorkBench.
    * Session: This is used for keep more than one session in the app frontend.

    Devicehub specially raises user awareness when an action
    has a Severity of ``Warning`` or greater.
    """

    Internal = 0
    External = 1
    Session = 2

    def __str__(self):
        return self.name
