# Copyright (C) 2026 Linuxfabrik <info@linuxfabrik.ch>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# On Debian systems, the complete text of the GNU General Public License
# version 2 can be found in /usr/share/common-licenses/GPL-2.
#
# SPDX-License-Identifier: GPL-2.0-or-later

"""Non-ORM value types: dataclasses, IntEnums, and custom SQLAlchemy type."""

import dataclasses
import enum
import ipaddress
import socket

import sqlalchemy.engine
import sqlalchemy.types


@dataclasses.dataclass
class InetAddr:
    """
    Wrapper for IPv4 / IPv6 addresses.

    Replaces C++ struct in_addr / in6_addr with Python ipaddress types.
    """

    address_family: int = socket.AF_INET
    ipv4: ipaddress.IPv4Address = dataclasses.field(
        default_factory=lambda: ipaddress.IPv4Address(0),
    )
    ipv6: ipaddress.IPv6Address = dataclasses.field(
        default_factory=lambda: ipaddress.IPv6Address(0),
    )


@dataclasses.dataclass
class InetAddrMask:
    """Holder of an address / netmask pair."""

    address: InetAddr | None = None
    netmask: InetAddr | None = None
    broadcast_address: InetAddr | None = None
    network_address: InetAddr | None = None
    last_host: InetAddr | None = None


@dataclasses.dataclass
class Inet6AddrMask(InetAddrMask):
    """IPv6-specific address/mask pair."""

    pass


class TCPFlag(enum.IntEnum):
    """TCP header flag bits."""

    URG = 0
    ACK = 1
    PSH = 2
    RST = 3
    SYN = 4
    FIN = 5


class PolicyAction(enum.IntEnum):
    """Actions for policy rules."""

    Unknown = 0
    Accept = 1
    Reject = 2
    Deny = 3
    Scrub = 4
    Return = 5
    Skip = 6
    Continue = 7
    Accounting = 8
    Modify = 9
    Pipe = 10
    Custom = 11
    Branch = 12


class Direction(enum.IntEnum):
    """Traffic direction for policy rules."""

    Undefined = 0
    Inbound = 1
    Outbound = 2
    Both = 3


class NATAction(enum.IntEnum):
    """Actions for NAT rules."""

    Translate = 0
    Branch = 1


class NATRuleType(enum.IntEnum):
    """NAT rule classification."""

    Unknown = 0
    NONAT = 1
    NATBranch = 2
    SNAT = 3
    Masq = 4
    DNAT = 5
    SDNAT = 6
    SNetnat = 7
    DNetnat = 8
    Redirect = 9
    Return = 10
    Skip = 11
    Continue = 12
    LB = 13


class RoutingRuleType(enum.IntEnum):
    """Routing rule classification."""

    Undefined = 0
    SinglePath = 1
    MultiPath = 2


class StandardId(enum.IntEnum):
    """Well-known object IDs in the database."""

    ROOT = 0
    ANY_ADDRESS = 1
    ANY_SERVICE = 2
    ANY_INTERVAL = 3
    STANDARD_LIB = 4
    USER_LIB = 5
    TEMPLATE_LIB = 6
    DELETED_OBJECTS = 7
    DUMMY_ADDRESS = 8
    DUMMY_SERVICE = 9
    DUMMY_INTERFACE = 10


class JSONEncodedSet(sqlalchemy.types.TypeDecorator):
    """Stores a Python ``set[str]`` as a JSON list in the database."""

    impl = sqlalchemy.types.JSON
    cache_ok = True

    def process_bind_param(
        self, value: set[str] | None, dialect: sqlalchemy.engine.Dialect
    ) -> list[str] | None:
        if value is not None:
            return sorted(value)
        return value

    def process_result_value(
        self, value: list[str] | None, dialect: sqlalchemy.engine.Dialect
    ) -> set[str] | None:
        if value is not None:
            return set(value)
        return value
