package extnet

import (
	"context"
	"encoding/json"
	"net/http"
	"time"

	"repository.basistech.ru/BASIS/decort-golang-sdk/internal/constants"
	"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)

// CreateRequest struct for creating account
type CreateRequest struct {
	// Name of the bridge network
	// Required: true
	BridgeNetworkName string `url:"bridge_network_name" json:"bridge_network_name" validate:"required"`

	// Detailed description of the external network
	// Required: true
	Description string `url:"description" json:"description" validate:"required"`

	// User-friendly name for the external network
	// Required: true
	DisplayName string `url:"display_name" json:"display_name" validate:"required"`

	// Whether the network is enabled
	// Required: true
	Enabled bool `url:"enabled" json:"enabled"`

	// List of hypervisor names
	// Required: true
	Hypervisors []string `url:"hypervisors" json:"hypervisors" validate:"required"`

	// List of external network ports
	// Required: false
	ExternalNetworkPorts []ExternalNetworkPortRequest `url:"-" json:"external_network_ports,omitempty"`

	// IPv4 default gateway address
	// Required: false
	DefaultGatewayIPv4 string `url:"default_gateway_ipv4,omitempty" json:"default_gateway_ipv4,omitempty"`

	// IPv6 default gateway address
	// Required: false
	DefaultGatewayIPv6 string `url:"default_gateway_ipv6,omitempty" json:"default_gateway_ipv6,omitempty"`

	// IPv4 subnet in CIDR notation (Either subnet_v4 or subnet_v6 must be specified)
	// Required: false
	SubnetV4 string `url:"subnet_v4,omitempty" json:"subnet_v4,omitempty"`

	// IPv6 subnet in CIDR notation (Either subnet_v4 or subnet_v6 must be specified)
	// Required: false
	SubnetV6 string `url:"subnet_v6,omitempty" json:"subnet_v6,omitempty"`

	// VLAN tag identifier
	// Required: false
	VLANTag string `url:"vlan_tag,omitempty" json:"vlan_tag,omitempty" validate:"omitempty,trunkTags"`
}

type ExternalNetworkPortRequest struct {
	// Access group ID
	// Required: true
	AccessGroupID string `url:"access_group_id" json:"access_group_id" validate:"required"`

	// Access group name
	// Required: false
	AccessGroupName string `url:"access_group_name,omitempty" json:"access_group_name,omitempty"`

	// Comment for the external network port
	// Required: true
	Comment string `url:"comment" json:"comment" validate:"required"`

	// User-friendly name for the external network port
	// Required: true
	DisplayName string `url:"display_name" json:"display_name" validate:"required"`

	// Whether the network pork is enabled
	// Required: true
	Enabled bool `url:"enabled" json:"enabled"`

	// IPv4
	// Required: false
	IPv4 string `url:"ipv4,omitempty" json:"ipv4,omitempty"`

	// IPv6
	// Required: false
	IPv6 string `url:"ipv6,omitempty" json:"ipv6,omitempty"`

	// IPv6 Config
	// Required: false
	IPv6Config *IPv6ConfigRequest `url:"-" json:"ipv6_config,omitempty"`

	// MAC address
	// Required: true
	MAC string `url:"mac" json:"mac" validate:"required"`

	// Router gateway port
	// Required: false
	RouterGatewayPort *RouterGatewayPortRequest `url:"-" json:"router_gateway_port,omitempty"`

	// Floating IP
	// Required: false
	FloatingIP *FloatingIPRequest `url:"-" json:"floating_ip,omitempty"`
}

type IPv6ConfigRequest struct {
	//Address Mode (Slaac or DhcpV6Stateful)
	// Required: true
	AddressMode string `url:"address_mode" json:"address_mode" validate:"required"`

	// If true, the port will periodically send RA packets.
	// Required: true
	EnablePeriodicRa bool `url:"enable_periodic_ra" json:"enable_periodic_ra"`

	// The number of waiting seconds between sending periodic RA
	// Required: true
	IntervalRa int64 `url:"interval_ra" json:"interval_ra" validate:"required"`

	// The Default Router Preference (PRF) indicates whether this router should be preferred over other default routers.
	// high, low, medium
	// Required: true
	RouterPreference string `url:"router_preference" json:"router_preference" validate:"required"`
}

type RouterGatewayPortRequest struct {
	// Created at
	// Required: true
	CreatedAt time.Time `url:"created_at" json:"created_at" validate:"required"`

	// Description
	// Required: true
	Description string `url:"description" json:"description" validate:"required"`

	// Port id
	// Required: true
	ID string `url:"id" json:"id" validate:"required"`

	// User-friendly name for the external network port
	// Required: true
	RouterDisplayName string `url:"router_display_name" json:"router_display_name" validate:"required"`

	// Router ID
	// Required: true
	RouterID string `url:"router_id" json:"router_id" validate:"required"`

	// SNAT Enabled
	// Required: true
	SNATEnabled bool `url:"snat_enabled" json:"snat_enabled"`

	// Updated at
	// Required: true
	UpdatedAt time.Time `url:"updated_at" json:"updated_at" validate:"required"`
}

type FloatingIPRequest struct {
	// Access group ID
	// Required: true
	AccessGroupID string `url:"access_group_id" json:"access_group_id" validate:"required"`

	// Access group name
	// Required: true
	AccessGroupName string `url:"access_group_name" json:"access_group_name" validate:"required"`

	// Created at
	// Required: true
	CreatedAt time.Time `url:"created_at" json:"created_at" validate:"required"`

	// External network port
	// Required: true
	ExternalNetworkPort string `url:"external_network_port" json:"external_network_port" validate:"required"`

	// ID of the Floating IP
	// Required: true
	ID string `url:"id" json:"id" validate:"required"`

	// Logical port
	// Required: false
	LogicalPort *LogicalPortRequest `url:"-" json:"logical_port,omitempty"`

	// Router
	// Required: true
	Router *RouterRequest `url:"-" json:"router" validate:"required"`

	// Updated at
	// Required: true
	UpdatedAt time.Time `url:"updated_at" json:"updated_at" validate:"required"`

	// ID of version
	// Required: true
	VersionID uint64 `url:"version_id" json:"version_id" validate:"required"`
}

type LogicalPortRequest struct {
	// Logical Port ID
	// Required: true
	ID string `url:"id" json:"id" validate:"required"`

	// Access group ID
	// Required: true
	AccessGroupID string `url:"access_group_id" json:"access_group_id" validate:"required"`

	// Access group name
	// Required: true
	AccessGroupName string `url:"access_group_name" json:"access_group_name" validate:"required"`

	// MAC of adapter
	// Required: true
	AdapterMAC string `url:"adapter_mac" json:"adapter_mac" validate:"required"`

	// Address detection
	// Required: true
	AddressDetection bool `url:"address_detection" json:"address_detection" validate:"required"`

	// Description
	// Required: true
	Description string `url:"description" json:"description" validate:"required"`

	// Created at
	// Required: false
	CreatedAt time.Time `url:"created_at,omitempty" json:"created_at,omitempty"`

	// User-friendly name for router
	// Required: true
	RouterDisplayName string `url:"router_display_name" json:"router_display_name" validate:"required"`

	// Whether the logical pork is enabled
	// Required: true
	Enabled bool `url:"enabled" json:"enabled"`

	// External Network ID
	// Required: false
	ExternalNetworkID string `url:"external_network_id,omitempty" json:"external_network_id,omitempty"`

	// Hypervisor
	// Required: true
	Hypervisor string `url:"hypervisor" json:"hypervisor" validate:"required"`

	// User-friendly name for hypervisor
	// Required: false
	HypervisorDisplayName string `url:"hypervisor_display_name,omitempty" json:"hypervisor_display_name,omitempty"`

	// Live Migration Target Hv
	// Required: true
	LiveMigrationTargetHV string `url:"live_migration_target_hv" json:"live_migration_target_hv" validate:"required"`

	// Status
	// Required: true
	Status *StatusRequest `url:"-" json:"status" validate:"required"`

	// Port bindings
	// Required: true
	Bindings *PortBindingsRequest `url:"-" json:"bindings" validate:"required"`

	// Unique Identifier
	// Required: true
	UniqueIDentifier string `url:"unique_identifier" json:"unique_identifier" validate:"required"`

	// Updated at
	// Required: true
	UpdatedAt time.Time `url:"updated_at" json:"updated_at" validate:"required"`

	// ID of version
	// Required: true
	VersionID uint64 `url:"version_id" json:"version_id" validate:"required"`
}

type StatusRequest struct {
	// Common
	// Required: true
	Common string `url:"common" json:"common" validate:"required"`

	// Hypervisors status
	// Required: false
	Hypervisors []HypervisorStatusRequest `url:"-" json:"hypervisors,omitempty"`
}

type PortBindingsRequest struct {
	// Binding ID
	// Required: true
	ID string `url:"id" json:"id" validate:"required"`

	// User-friendly name for segment
	// Required: true
	SegmentDisplayName string `url:"segment_display_name" json:"segment_display_name" validate:"required"`

	// Segment ID
	// Required: true
	SegmentID string `url:"segment_id" json:"segment_id" validate:"required"`

	// Port security
	// Required: true
	PortSecurity bool `url:"port_security" json:"port_security" validate:"required"`

	// Address detection
	// Required: true
	AddressDetection bool `url:"address_detection" json:"address_detection" validate:"required"`

	// Is Exclude From Firewall
	// Required: true
	IsExcludedFromFirewall bool `url:"is_excluded_from_firewall" json:"is_excluded_from_firewall" validate:"required"`

	// ID of version
	// Required: true
	VersionID uint64 `url:"version_id" json:"version_id" validate:"required"`

	// Created at
	// Required: true
	CreatedAt time.Time `url:"created_at" json:"created_at" validate:"required"`

	// Updated at
	// Required: true
	UpdatedAt time.Time `url:"updated_at" json:"updated_at" validate:"required"`

	// Logical port addresses
	// Required: true
	LogicalPortAddresses []LogicalPortAddressRequest `url:"-" json:"logical_port_addresses" validate:"required"`
}

type HypervisorStatusRequest struct {
	// Status
	// Required: true
	Status string `url:"status" json:"status" validate:"required"`

	// Name of hypervisor
	// Required: true
	Name string `url:"name" json:"name" validate:"required"`

	// User-friendly name for the hypervisor
	// Required: true
	DisplayName string `url:"display_name" json:"display_name" validate:"required"`

	// Hypervisor status
	// Required: true
	HypervisorStatus string `url:"hypervisor_status" json:"hypervisor_status" validate:"required"`

	// Synced at
	// Required: true
	SyncedAt time.Time `url:"synced_at" json:"synced_at" validate:"required"`
}

type LogicalPortAddressRequest struct {
	// IP of port
	// Required: true
	IP string `url:"ip" json:"ip" validate:"required"`

	// IP type (IPv4 or IPv6)
	// Required: true
	IPType string `url:"ip_type" json:"ip_type" validate:"required"`

	// Is discovered
	// Required: false
	IsDiscovered bool `url:"is_discovered,omitempty" json:"is_discovered,omitempty"`

	// Is discovered
	// Required: true
	IsPrimary bool `url:"is_primary" json:"is_primary" validate:"required"`

	// MAC
	// Required: false
	MAC string `url:"mac,omitempty" json:"mac,omitempty"`

	// ID
	// Required: true
	ID string `url:"id" json:"id" validate:"required"`

	// Logical port id
	// Required: true
	LogicalPortID string `url:"logical_port_id" json:"logical_port_id" validate:"required"`

	// Assigned at
	// Required: false
	AssignedAt time.Time `url:"assigned_at,omitempty" json:"assigned_at,omitempty"`
}

type RouterRequest struct {
	// Access group ID
	// Required: false
	AccessGroupID string `url:"access_group_id,omitempty" json:"access_group_id,omitempty"`

	// Access group name
	// Required: false
	AccessGroupName string `url:"access_group_name,omitempty" json:"access_group_name,omitempty"`

	// Created at
	// Required: false
	CreatedAt time.Time `url:"created_at,omitempty" json:"created_at,omitempty"`

	// Detailed description of the router
	// Required: false
	Description string `url:"description,omitempty" json:"description,omitempty"`

	// User-friendly name for the router
	// Required: true
	DisplayName string `url:"display_name" json:"display_name" validate:"required"`

	// Whether the router is enabled
	// Required: false
	Enabled bool `url:"enabled,omitempty" json:"enabled,omitempty"`

	// Gateway ports
	// Required: false
	GatewayPorts []GatewayPortRequest `url:"-" json:"gateway_ports,omitempty"`

	// ID
	// Required: true
	ID string `url:"id" json:"id" validate:"required"`

	// Policies
	// Required: false
	Policies []RouterPolicyRequest `url:"-" json:"policies,omitempty"`

	// Ports
	// Required: false
	Ports []RouterPortRequest `url:"-" json:"ports,omitempty"`

	// Status
	// Required: false
	Status *StatusRequest `url:"-" json:"status,omitempty"`

	// Updated at
	// Required: false
	UpdatedAt time.Time `url:"updated_at,omitempty" json:"updated_at,omitempty"`

	// ID of version
	// Required: false
	VersionID uint64 `url:"version_id,omitempty" json:"version_id,omitempty"`
}

type GatewayPortRequest struct {
	// Created at
	// Required: true
	CreatedAt time.Time `url:"created_at" json:"created_at" validate:"required"`

	// Description
	// Required: true
	Description string `url:"description" json:"description" validate:"required"`

	// L4 port max
	// Required: false
	ExternalL4PortMax int64 `url:"external_l4_port_max,omitempty" json:"external_l4_port_max,omitempty"`

	// L4 port min
	// Required: false
	ExternalL4PortMin int64 `url:"external_l4_port_min,omitempty" json:"external_l4_port_min,omitempty"`

	// External network port
	// Required: true
	ExternalNetworkPort interface{} `url:"external_network_port" json:"external_network_port" validate:"required"`

	// ID of port
	// Required: false
	ID string `url:"id,omitempty" json:"id,omitempty"`

	// SNAT Enabled
	// Required: true
	SNATEnabled bool `url:"snat_enabled" json:"snat_enabled"`

	// Status
	// Required: false
	Status *StatusRequest `url:"-" json:"status,omitempty"`

	// Updated at
	// Required: true
	UpdatedAt time.Time `url:"updated_at" json:"updated_at" validate:"required"`

	// ID of version
	// Required: true
	VersionID uint64 `url:"version_id" json:"version_id" validate:"required"`
}

type RouterPolicyRequest struct {
	// Action
	// Required: true
	Action string `url:"action" json:"action" validate:"required"`

	// Created at
	// Required: true
	CreatedAt time.Time `url:"created_at" json:"created_at" validate:"required"`

	// User-friendly name for the policy
	// Required: true
	DisplayName string `url:"display_name" json:"display_name" validate:"required"`

	// Whether the policy is enabled
	// Required: false
	Enabled bool `url:"enabled,omitempty" json:"enabled,omitempty"`

	// ID of port
	// Required: false
	ID string `url:"id,omitempty" json:"id,omitempty"`

	// Match
	// Required: true
	Match interface{} `url:"match" json:"match" validate:"required"`

	// Next IPv4 address
	// Required: true
	NextIPv4Address []string `url:"next_ipv4_address" json:"next_ipv4_address" validate:"required"`

	// Next IPv6 address
	// Required: true
	NextIPv6Address []string `url:"next_ipv6_address" json:"next_ipv6_address" validate:"required"`

	// Priority
	// Required: true
	Priority int64 `url:"priority" json:"priority" validate:"required"`

	// Updated at
	// Required: true
	UpdatedAt time.Time `url:"updated_at" json:"updated_at" validate:"required"`

	// ID of version
	// Required: true
	VersionID uint64 `url:"version_id" json:"version_id" validate:"required"`
}

type RouterPortRequest struct {
	// Created at
	// Required: true
	CreatedAt time.Time `url:"created_at" json:"created_at" validate:"required"`

	// Detailed description of the router port
	// Required: true
	Description string `url:"description" json:"description" validate:"required"`

	// Whether the router port is enabled
	// Required: true
	Enabled bool `url:"enabled" json:"enabled"`

	// ID of port
	// Required: false
	ID string `url:"id,omitempty" json:"id,omitempty"`

	// Next IPv4 address
	// Required: true
	NextIPv4Address []string `url:"next_ipv4_address" json:"next_ipv4_address" validate:"required"`

	// Next IPv6 address
	// Required: true
	NextIPv6Address []string `url:"next_ipv6_address" json:"next_ipv6_address" validate:"required"`

	// IPv6 Config
	// Required: true
	IPv6Config *IPv6ConfigRequest `url:"-" json:"ipv6_config" validate:"required"`

	// MAC address
	// Required: true
	MAC string `url:"mac" json:"mac" validate:"required"`

	// Segment
	// Required: true
	Segment *SegmentRequest `url:"-" json:"segment" validate:"required"`

	// Segment ID
	// Required: true
	SegmentID string `url:"segment_id" json:"segment_id" validate:"required"`

	// Status
	// Required: false
	Status *StatusRequest `url:"-" json:"status,omitempty"`

	// Updated at
	// Required: true
	UpdatedAt time.Time `url:"updated_at" json:"updated_at" validate:"required"`

	// ID of version
	// Required: true
	VersionID uint64 `url:"version_id" json:"version_id" validate:"required"`
}

type SegmentRequest struct {
	// Access group ID
	// Required: true
	AccessGroupID string `url:"access_group_id" json:"access_group_id" validate:"required"`

	// Access group name
	// Required: false
	AccessGroupName string `url:"access_group_name,omitempty" json:"access_group_name,omitempty"`

	// Created at
	// Required: true
	CreatedAt time.Time `url:"created_at" json:"created_at" validate:"required"`

	// Detailed description of the router port
	// Required: true
	Description string `url:"description" json:"description" validate:"required"`

	// DHCP IPv4
	// Required: false
	DHCPv4 *DHCPv4ConfigRequest `url:"-" json:"dhcp_v4,omitempty"`

	// DHCP IPv6
	// Required: false
	DHCPv6 *DHCPv6ConfigRequest `url:"-" json:"dhcp_v6,omitempty"`

	// User-friendly name for the segment
	// Required: true
	DisplayName string `url:"display_name" json:"display_name" validate:"required"`

	// Whether the segment is enabled
	// Required: false
	Enabled bool `url:"enabled,omitempty" json:"enabled,omitempty"`

	// ID of segment
	// Required: false
	ID string `url:"id,omitempty" json:"id,omitempty"`

	// Logical ports info
	// Required: false
	LogicalPortsInfo []EntityInfoRequest `url:"-" json:"logical_ports_info,omitempty"`

	// Routers info
	// Required: false
	RoutersInfo []EntityInfoRequest `url:"-" json:"routers_info,omitempty"`

	// Status
	// Required: false
	Status *StatusRequest `url:"-" json:"status,omitempty"`

	// IPv4 subnet in CIDR notation (Either subnet_v4 or subnet_v6 must be specified)
	// Required: false
	SubnetV4 string `url:"subnet_v4,omitempty" json:"subnet_v4,omitempty"`

	// IPv6 subnet in CIDR notation (Either subnet_v4 or subnet_v6 must be specified)
	// Required: false
	SubnetV6 string `url:"subnet_v6,omitempty" json:"subnet_v6,omitempty"`

	// Updated at
	// Required: true
	UpdatedAt time.Time `url:"updated_at" json:"updated_at" validate:"required"`

	// ID of version
	// Required: true
	VersionID uint64 `url:"version_id" json:"version_id" validate:"required"`
}

type DHCPv4ConfigRequest struct {
	// DNS
	// Required: false
	DNS []string `url:"dns,omitempty" json:"dns,omitempty"`

	// Excluded address ranges
	// Required: false
	ExcludedAddressRanges []string `url:"excluded_address_ranges,omitempty" json:"excluded_address_ranges,omitempty"`

	// Gateway
	// Required: true
	Gateway string `url:"gateway" json:"gateway" validate:"required"`

	// ID of config
	// Required: false
	ID string `url:"id,omitempty" json:"id,omitempty"`

	// Lease time
	// Required: false
	LeaseTime int64 `url:"lease_time,omitempty" json:"lease_time,omitempty"`

	// Server IP
	// Required: true
	ServerIP string `url:"server_ip" json:"server_ip" validate:"required"`

	// Server MAC
	// Required: false
	ServerMAC string `url:"server_mac,omitempty" json:"server_mac,omitempty"`

	// Whether the config is enabled
	// Required: true
	Enabled bool `url:"enabled" json:"enabled"`
}

type DHCPv6ConfigRequest struct {
	// Address prefix
	// Required: true
	AddressPrefix string `url:"address_prefix" json:"address_prefix" validate:"required"`

	// DNS
	// Required: false
	DNS []string `url:"dns,omitempty" json:"dns,omitempty"`

	// ID of config
	// Required: false
	ID string `url:"id,omitempty" json:"id,omitempty"`

	// Lease time
	// Required: true
	LeaseTime int64 `url:"lease_time" json:"lease_time" validate:"required"`

	// Server MAC
	// Required: true
	ServerMAC string `url:"server_mac" json:"server_mac" validate:"required"`

	// Whether the config is enabled
	// Required: true
	Enabled bool `url:"enabled" json:"enabled"`
}

type EntityInfoRequest struct {
	// User-friendly name for the entity
	// Required: true
	DisplayName string `url:"display_name" json:"display_name" validate:"required"`

	// ID of entity
	// Required: false
	ID string `url:"id,omitempty" json:"id,omitempty"`
}

// Create creates extnet
func (e ExtNet) Create(ctx context.Context, req CreateRequest) (*ExternalNetworkResponse, error) {
	err := validators.ValidateRequest(req)
	if err != nil {
		return nil, validators.ValidationErrors(validators.GetErrors(err))
	}

	url := "/sdn/external_network/create"

	res, err := e.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req)
	if err != nil {
		return nil, err
	}

	info := ExternalNetworkResponse{}

	err = json.Unmarshal(res, &info)
	if err != nil {
		return nil, err
	}

	return &info, nil
}
