from apiflask import fields, Schema
from marshmallow import ValidationError

from hiddifypanel.models import DomainType, ProxyProto, ProxyL3, ProxyTransport, ProxyCDN, ConfigEnum, ChildMode
from hiddifypanel.panel.commercial.restapi.v2.admin.schema import UserSchema, AdminSchema


def hconfig_key_validator(value):
    if value not in [c.name for c in ConfigEnum]:
        raise ValidationError(f"{value} is not a valid hconfig key.")
    return value


class DomainSchema(Schema):
    child_unique_id = fields.String( metadata={"description": "The child's unique id"})
    domain = fields.String(required=True,  metadata={"description": "The domain name"})
    alias = fields.String( metadata={"description": "The domain alias"}, allow_none=True)
    sub_link_only = fields.Boolean(required=True,  metadata={"description": "Is the domain sub link only"})
    mode = fields.Enum(DomainType, required=True,  metadata={"description": "The domain type"})
    cdn_ip = fields.String( metadata={"description": "The cdn ip"}, allow_none=True)
    grpc = fields.Boolean(required=True,  metadata={"description": "Is the domain grpc"})
    servernames = fields.String( metadata={"description": "The servernames"}, allow_none=True)
    show_domains = fields.List(fields.String(),  metadata={"description":"The list of domains to show"})


class ProxySchema(Schema):
    child_unique_id = fields.String( metadata={"description": "The child's unique id"})
    name = fields.String(required=True,  metadata={"description": "The proxy name"})
    enable = fields.Boolean(required=True,  metadata={"description": "Is the proxy enabled"})
    proto = fields.Enum(ProxyProto, required=True,  metadata={"description": "The proxy protocol"})
    l3 = fields.Enum(ProxyL3, required=True,  metadata={"description": "The proxy l3"})
    transport = fields.Enum(ProxyTransport, required=True,  metadata={"description": "The proxy transport"})
    cdn = fields.Enum(ProxyCDN, required=True,  metadata={"description": "The proxy cdn"})


class StringOrBooleanField(fields.Field):
    def _deserialize(self, value, attr, data, **kwargs):
        if isinstance(value, (str, bool)):
            return str(value)
        else:
            raise ValidationError("Value must be a string or a boolean.")

    def _serialize(self, value, attr, obj, **kwargs):
        return value


class HConfigSchema(Schema):
    child_unique_id = fields.String( metadata={"description": "The child's unique id"})
    key = fields.String(required=True,  metadata={"description": "The config key"}, validate=hconfig_key_validator)  # type: ignore
    value = StringOrBooleanField(required=True,  metadata={"description": "The config value"})


# region usage
class UsageData(Schema):
    uuid = fields.UUID(required=True,  metadata={"description": "The user uuid"})
    usage = fields.Integer(required=True,  metadata={"description": "The user usage in bytes"})
    devices = fields.List(fields.String(required=True,  metadata={"description": "The user connected devices"}))


class UsageInputOutputSchema(Schema):
    usages = fields.List(fields.Nested(UsageData), required=True,  metadata={"description": "The list of usages"})
# endregion


# region sync
class SyncInputSchema(Schema):
    domains = fields.List(fields.Nested(DomainSchema), required=False,  metadata={"description": "The list of domains"})
    proxies = fields.List(fields.Nested(ProxySchema), required=False,  metadata={"description": "The list of proxies"})
    hconfigs = fields.List(fields.Nested(HConfigSchema), required=False,  metadata={"description": "The list of configs"})
    # users = fields.List(fields.Nested(UserSchema),required=True, metadata={"description": "The list of users"})
    # admin_users = fields.List(fields.Nested(AdminSchema),required=True, metadata={"description": "The list of admin users"})

    def validate(self, data, **kwargs):
        if not (data.get("domains") or data.get("proxies") or data.get("hconfigs")):
            raise ValidationError("At least one field must exist (domains, proxies, or hconfigs)")
        return data


class SyncOutputSchema(Schema):
    users = fields.List(fields.Nested(UserSchema), required=True,  metadata={"description": "The list of users"})
    admin_users = fields.List(fields.Nested(AdminSchema), required=True,  metadata={"description": "The list of admin users"})

# endregion


# region child status
class ChildStatusInputSchema(Schema):
    child_unique_id = fields.String(required=True,  metadata={"description": "The child's unique id"})


class ChildStatusOutputSchema(Schema):
    existance = fields.Boolean(required=True,  metadata={"description": "Whether child exists"})

# end region


# region register

class RegisterDataSchema(Schema):
    users = fields.List(fields.Nested(UserSchema), required=True,  metadata={"description": "The list of users"})
    domains = fields.List(fields.Nested(DomainSchema), required=True,  metadata={"description": "The list of domains"})
    proxies = fields.List(fields.Nested(ProxySchema), required=True,  metadata={"description": "The list of proxies"})
    admin_users = fields.List(fields.Nested(AdminSchema), required=True,  metadata={"description": "The list of admin users"})
    hconfigs = fields.List(fields.Nested(HConfigSchema), required=True,  metadata={"description": "The list of configs"})


class RegisterInputSchema(Schema):
    panel_data = fields.Nested(RegisterDataSchema, required=True,  metadata={"description": "The child's data"})
    unique_id = fields.String(required=True,  metadata={"description": "The child's unique id"})
    name = fields.String(required=True,  metadata={"description": "The child's name"})
    mode = fields.Enum(ChildMode, required=True,  metadata={"description": "The child's mode"})


class RegisterOutputSchema(Schema):
    parent_unique_id = fields.String( metadata={"description": "The parent's unique id"})
    users = fields.List(fields.Nested(UserSchema), required=True,  metadata={"description": "The list of users"})
    admin_users = fields.List(fields.Nested(AdminSchema), required=True,  metadata={"description": "The list of admin users"})

# endregion
