from typing import Callable, Iterable, Tuple

from teal.resource import Converters, Resource

from ereuse_devicehub.resources.device import schemas
from ereuse_devicehub.resources.device.models import Manufacturer
from ereuse_devicehub.resources.device.views import DeviceView, DeviceMergeView, ManufacturerView


class DeviceDef(Resource):
    SCHEMA = schemas.Device
    VIEW = DeviceView
    ID_CONVERTER = Converters.string
    AUTH = False  # We manage this at each view

    def __init__(self, app,
                 import_name=__name__,
                 static_folder='static',
                 static_url_path=None,
                 template_folder='templates',
                 url_prefix=None,
                 subdomain=None,
                 url_defaults=None,
                 root_path=None,
                 cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
        super().__init__(app, import_name, static_folder, static_url_path, template_folder,
                         url_prefix, subdomain, url_defaults, root_path, cli_commands)

        device_merge = DeviceMergeView.as_view('merge-devices', definition=self, auth=app.auth)

        if self.AUTH:
            device_merge = app.auth.requires_auth(device_merge)

        path = '/<{value}:dev1_id>/merge/<{value}:dev2_id>'.format(value=self.ID_CONVERTER.value)

        # self.add_url_rule(path, view_func=device_merge, methods={'POST'})


class ComputerDef(DeviceDef):
    VIEW = None
    SCHEMA = schemas.Computer

    def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
                 template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
                 root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
        super().__init__(app, import_name, static_folder, static_url_path, template_folder,
                         url_prefix, subdomain, url_defaults, root_path, cli_commands)


class DesktopDef(ComputerDef):
    VIEW = None
    SCHEMA = schemas.Desktop


class LaptopDef(ComputerDef):
    VIEW = None
    SCHEMA = schemas.Laptop


class ServerDef(ComputerDef):
    VIEW = None
    SCHEMA = schemas.Server


class MonitorDef(DeviceDef):
    VIEW = None
    SCHEMA = schemas.Monitor

    def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
                 template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
                 root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
        super().__init__(app, import_name, static_folder, static_url_path, template_folder,
                         url_prefix, subdomain, url_defaults, root_path, cli_commands)


class ComputerMonitorDef(MonitorDef):
    VIEW = None
    SCHEMA = schemas.ComputerMonitor


class TelevisionSetDef(MonitorDef):
    VIEW = None
    SCHEMA = schemas.TelevisionSet


class MobileDef(DeviceDef):
    VIEW = None
    SCHEMA = schemas.Mobile

    def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
                 template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
                 root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
        super().__init__(app, import_name, static_folder, static_url_path, template_folder,
                         url_prefix, subdomain, url_defaults, root_path, cli_commands)


class SmartphoneDef(MobileDef):
    VIEW = None
    SCHEMA = schemas.Smartphone


class TabletDef(MobileDef):
    VIEW = None
    SCHEMA = schemas.Tablet


class CellphoneDef(MobileDef):
    VIEW = None
    SCHEMA = schemas.Cellphone


class ComponentDef(DeviceDef):
    VIEW = None
    SCHEMA = schemas.Component

    def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
                 template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
                 root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
        super().__init__(app, import_name, static_folder, static_url_path, template_folder,
                         url_prefix, subdomain, url_defaults, root_path, cli_commands)


class GraphicCardDef(ComponentDef):
    VIEW = None
    SCHEMA = schemas.GraphicCard


class DataStorageDef(ComponentDef):
    VIEW = None
    SCHEMA = schemas.DataStorage


class HardDriveDef(DataStorageDef):
    VIEW = None
    SCHEMA = schemas.HardDrive


class SolidStateDriveDef(DataStorageDef):
    VIEW = None
    SCHEMA = schemas.SolidStateDrive


class MotherboardDef(ComponentDef):
    VIEW = None
    SCHEMA = schemas.Motherboard


class NetworkAdapterDef(ComponentDef):
    VIEW = None
    SCHEMA = schemas.NetworkAdapter


class RamModuleDef(ComponentDef):
    VIEW = None
    SCHEMA = schemas.RamModule


class ProcessorDef(ComponentDef):
    VIEW = None
    SCHEMA = schemas.Processor


class SoundCardDef(ComponentDef):
    VIEW = None
    SCHEMA = schemas.SoundCard


class DisplayDef(ComponentDef):
    VIEW = None
    SCHEMA = schemas.Display


class BatteryDef(ComponentDef):
    VIEW = None
    SCHEMA = schemas.Battery


class CameraDef(ComponentDef):
    VIEW = None
    SCHEMA = schemas.Camera


class ComputerAccessoryDef(DeviceDef):
    VIEW = None
    SCHEMA = schemas.ComputerAccessory

    def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
                 template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
                 root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
        super().__init__(app, import_name, static_folder, static_url_path, template_folder,
                         url_prefix, subdomain, url_defaults, root_path, cli_commands)


class MouseDef(ComputerAccessoryDef):
    VIEW = None
    SCHEMA = schemas.Mouse


class KeyboardDef(ComputerAccessoryDef):
    VIEW = None
    SCHEMA = schemas.Keyboard


class SAIDef(ComputerAccessoryDef):
    VIEW = None
    SCHEMA = schemas.SAI


class MemoryCardReaderDef(ComputerAccessoryDef):
    VIEW = None
    SCHEMA = schemas.MemoryCardReader


class NetworkingDef(DeviceDef):
    VIEW = None
    SCHEMA = schemas.Networking

    def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
                 template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
                 root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
        super().__init__(app, import_name, static_folder, static_url_path, template_folder,
                         url_prefix, subdomain, url_defaults, root_path, cli_commands)


class RouterDef(NetworkingDef):
    VIEW = None
    SCHEMA = schemas.Router


class SwitchDef(NetworkingDef):
    VIEW = None
    SCHEMA = schemas.Switch


class HubDef(NetworkingDef):
    VIEW = None
    SCHEMA = schemas.Hub


class WirelessAccessPointDef(NetworkingDef):
    VIEW = None
    SCHEMA = schemas.WirelessAccessPoint


class PrinterDef(DeviceDef):
    VIEW = None
    SCHEMA = schemas.Printer

    def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
                 template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
                 root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
        super().__init__(app, import_name, static_folder, static_url_path, template_folder,
                         url_prefix, subdomain, url_defaults, root_path, cli_commands)


class LabelPrinterDef(PrinterDef):
    VIEW = None
    SCHEMA = schemas.LabelPrinter


class SoundDef(DeviceDef):
    VIEW = None
    SCHEMA = schemas.Sound

    def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
                 template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
                 root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
        super().__init__(app, import_name, static_folder, static_url_path, template_folder,
                         url_prefix, subdomain, url_defaults, root_path, cli_commands)


class MicrophoneDef(SoundDef):
    VIEW = None
    SCHEMA = schemas.Microphone


class VideoDef(DeviceDef):
    VIEW = None
    SCHEMA = schemas.Video

    def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
                 template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
                 root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
        super().__init__(app, import_name, static_folder, static_url_path, template_folder,
                         url_prefix, subdomain, url_defaults, root_path, cli_commands)


class VideoScalerDef(VideoDef):
    VIEW = None
    SCHEMA = schemas.VideoScaler


class VideoconferenceDef(VideoDef):
    VIEW = None
    SCHEMA = schemas.Videoconference


class CookingDef(DeviceDef):
    VIEW = None
    SCHEMA = schemas.Cooking

    def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
                 template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
                 root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
        super().__init__(app, import_name, static_folder, static_url_path, template_folder,
                         url_prefix, subdomain, url_defaults, root_path, cli_commands)


class Mixer(CookingDef):
    VIEW = None
    SCHEMA = schemas.Mixer


class DIYAndGardeningDef(DeviceDef):
    VIEW = None
    SCHEMA = schemas.DIYAndGardening

    def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
                 template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
                 root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
        super().__init__(app, import_name, static_folder, static_url_path, template_folder,
                         url_prefix, subdomain, url_defaults, root_path, cli_commands)


class DrillDef(DIYAndGardeningDef):
    VIEW = None
    SCHEMA = schemas.Drill


class PackOfScrewdriversDef(DIYAndGardeningDef):
    VIEW = None
    SCHEMA = schemas.PackOfScrewdrivers

    def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
                 template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
                 root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
        super().__init__(app, import_name, static_folder, static_url_path, template_folder,
                         url_prefix, subdomain, url_defaults, root_path, cli_commands)


class HomeDef(DeviceDef):
    VIEW = None
    SCHEMA = schemas.Home

    def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
                 template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
                 root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
        super().__init__(app, import_name, static_folder, static_url_path, template_folder,
                         url_prefix, subdomain, url_defaults, root_path, cli_commands)


class DehumidifierDef(HomeDef):
    VIEW = None
    SCHEMA = schemas.Dehumidifier


class StairsDef(HomeDef):
    VIEW = None
    SCHEMA = schemas.Stairs


class RecreationDef(DeviceDef):
    VIEW = None
    SCHEMA = schemas.Recreation

    def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
                 template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
                 root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
        super().__init__(app, import_name, static_folder, static_url_path, template_folder,
                         url_prefix, subdomain, url_defaults, root_path, cli_commands)


class BikeDef(RecreationDef):
    VIEW = None
    SCHEMA = schemas.Bike


class RacketDef(RecreationDef):
    VIEW = None
    SCHEMA = schemas.Racket


class ManufacturerDef(Resource):
    VIEW = ManufacturerView
    SCHEMA = schemas.Manufacturer
    AUTH = True

    def init_db(self, db: 'db.SQLAlchemy', exclude_schema=None):
        """Loads the manufacturers to the database."""
        if exclude_schema != 'common':
            Manufacturer.add_all_to_session(db.session)