package api

import (
	"strings"
	"time"
)

// ServiceLog represents internal service logging for diagnostics
type ServiceLog struct {
	Level     string            `json:"level"`
	Component string            `json:"component"`
	Message   string            `json:"message"`
	Details   map[string]string `json:"details,omitempty"`
	Timestamp int64             `json:"timestamp,omitempty"` // Unix nanoseconds (auto-set by logger)
	TraceID   string            `json:"trace_id,omitempty"`  // Optional distributed tracing ID
	ConnID    string            `json:"conn_id,omitempty"`   // Optional TCP flow identifier
}

// Config holds basic network and TLS configuration
type Config struct {
	Interface        string   `yaml:"interface"         json:"interface"`
	ListenPorts      []uint16 `yaml:"listen_ports"      json:"listen_ports"`
	BPFFilter        string   `yaml:"bpf_filter"        json:"bpf_filter,omitempty"`
	LocalIPs         []string `yaml:"local_ips"         json:"local_ips,omitempty"`          // Local IPs to monitor (empty = auto-detect, excludes loopback)
	ExcludeSourceIPs []string `yaml:"exclude_source_ips" json:"exclude_source_ips,omitempty"` // Source IPs or CIDR ranges to exclude (e.g., ["10.0.0.0/8", "192.168.1.1"])
	FlowTimeoutSec   int      `yaml:"flow_timeout_sec"  json:"flow_timeout_sec,omitempty"`   // Timeout for TLS handshake extraction (default: 30)
	PacketBufferSize int      `yaml:"packet_buffer_size" json:"packet_buffer_size,omitempty"` // Buffer size for packet channel (default: 1000)
	LogLevel         string   `yaml:"log_level"         json:"log_level,omitempty"`          // Log level: debug, info, warn, error (default: info)
}

// IPMeta contains IP metadata for stack fingerprinting
type IPMeta struct {
	TTL         uint8  `json:"ttl"`
	TotalLength uint16 `json:"total_length"`
	IPID        uint16 `json:"id"`
	DF          bool   `json:"df"`
}

// TCPMeta contains TCP metadata for stack fingerprinting
type TCPMeta struct {
	WindowSize  uint16   `json:"window_size"`
	MSS         uint16   `json:"mss,omitempty"`
	WindowScale uint8    `json:"window_scale,omitempty"`
	Options     []string `json:"options"`
	OptionKinds []uint8  `json:"-"` // Raw TCP option kind numbers for JA4T
}

// RawPacket represents a raw packet captured from the network
type RawPacket struct {
	Data      []byte `json:"-"`         // Raw packet data including link-layer header
	Timestamp int64  `json:"timestamp"` // nanoseconds since epoch
	LinkType  int    `json:"-"`         // Link type (1=Ethernet, 101=Linux SLL, etc.)
}

// TLSClientHello represents a client-side TLS ClientHello with IP/TCP metadata
type TLSClientHello struct {
	SrcIP      string  `json:"src_ip"`
	SrcPort    uint16  `json:"src_port"`
	DstIP      string  `json:"dst_ip"`
	DstPort    uint16  `json:"dst_port"`
	Payload    []byte  `json:"-"` // Not serialized
	IPMeta     IPMeta  `json:"ip_meta"`
	TCPMeta    TCPMeta `json:"tcp_meta"`
	ConnID     string  `json:"conn_id,omitempty"`     // Unique flow identifier
	SNI        string  `json:"tls_sni,omitempty"`     // Server Name Indication
	ALPN       string  `json:"tls_alpn,omitempty"`    // Application-Layer Protocol Negotiation
	TLSVersion string  `json:"tls_version,omitempty"` // Max TLS version supported
	SynToCHMs  *uint32 `json:"syn_to_clienthello_ms,omitempty"` // Time from SYN to ClientHello (ms)
}

// Fingerprints contains TLS fingerprints for a client flow
// Note: JA4Hash is kept for internal use but not serialized to LogRecord
// as the JA4 format already includes its own hash portions
type Fingerprints struct {
	JA4     string `json:"ja4"`
	JA4Hash string `json:"ja4_hash,omitempty"` // Internal use, not serialized to LogRecord
	JA4T    string `json:"ja4t,omitempty"`
	JA3     string `json:"ja3,omitempty"`
	JA3Hash string `json:"ja3_hash,omitempty"`
}

// LogRecord is the final log record, serialized as a flat JSON object
type LogRecord struct {
	SrcIP   string `json:"src_ip"`
	SrcPort uint16 `json:"src_port"`
	DstIP   string `json:"dst_ip"`
	DstPort uint16 `json:"dst_port"`

	// Flattened IPMeta fields
	IPTTL      uint8  `json:"ip_meta_ttl"`
	IPTotalLen uint16 `json:"ip_meta_total_length"`
	IPID       uint16 `json:"ip_meta_id"`
	IPDF       bool   `json:"ip_meta_df"`

	// Flattened TCPMeta fields
	TCPWindow  uint16  `json:"tcp_meta_window_size"`
	TCPMSS     *uint16 `json:"tcp_meta_mss,omitempty"`
	TCPWScale  *uint8  `json:"tcp_meta_window_scale,omitempty"`
	TCPOptions string  `json:"tcp_meta_options"` // comma-separated list

	// Correlation & Triage
	ConnID   string `json:"conn_id,omitempty"`    // Unique flow identifier
	SensorID string `json:"sensor_id,omitempty"`  // Sensor/captor identifier

	// TLS elements (ClientHello)
	TLSVersion string  `json:"tls_version,omitempty"` // Max TLS version announced by client
	SNI        string  `json:"tls_sni,omitempty"`     // Server Name Indication
	ALPN       string  `json:"tls_alpn,omitempty"`    // Application-Layer Protocol Negotiation

	// Behavioral detection (Timing)
	SynToCHMs *uint32 `json:"syn_to_clienthello_ms,omitempty"` // Time from SYN to ClientHello (ms)

	// Fingerprints
	// Note: ja4_hash is NOT included - the JA4 format already includes its own hash portions
	JA4     string `json:"ja4"`
	JA4T    string `json:"ja4t,omitempty"`
	JA3     string `json:"ja3,omitempty"`
	JA3Hash string `json:"ja3_hash,omitempty"`

	// Timestamp in nanoseconds since Unix epoch
	Timestamp int64 `json:"timestamp"`
}

// OutputConfig defines configuration for a single log output
type OutputConfig struct {
	Type        string            `yaml:"type"         json:"type"`              // unix_socket, stdout, file, etc.
	Enabled     bool              `yaml:"enabled"      json:"enabled"`           // whether this output is active
	AsyncBuffer int               `yaml:"async_buffer" json:"async_buffer"`      // queue size for async writes (e.g., 5000)
	Params      map[string]string `yaml:"params"       json:"params"`            // specific parameters like socket_path, path, etc.
}

// AppConfig is the complete ja4sentinel configuration
type AppConfig struct {
	Core    Config         `yaml:"core"    json:"core"`
	Outputs []OutputConfig `yaml:"outputs" json:"outputs"`
}

// Loader defines the interface for loading application configuration.
// Implementations must read configuration from a YAML file, merge with
// environment variables (JA4SENTINEL_*), and validate the final result.
type Loader interface {
	Load() (AppConfig, error)
}

// Capture defines the interface for capturing raw network packets.
// Implementations must listen on a configured network interface, apply
// BPF filters for specified ports, and emit RawPacket objects to a channel.
// The Close method must be called to release resources (e.g., pcap handle).
type Capture interface {
	Run(cfg Config, out chan<- RawPacket) error
	Close() error
	GetStats() (received, sent, dropped uint64)
}

// Parser defines the interface for extracting TLS ClientHello messages
// from raw network packets. Implementations must track TCP connection states,
// reassemble fragmented handshakes, and return TLSClientHello objects with
// IP/TCP metadata. Returns nil for non-TLS or non-ClientHello packets.
type Parser interface {
	Process(pkt RawPacket) (*TLSClientHello, error)
	Close() error
	GetMetrics() (retransmit, gapDetected, bufferExceeded, segmentExceeded uint64)
}

// Engine defines the interface for generating TLS fingerprints.
// Implementations must analyze TLS ClientHello payloads and produce
// JA4 (required) and optionally JA3 fingerprint strings.
type Engine interface {
	FromClientHello(ch TLSClientHello) (*Fingerprints, error)
}

// Writer defines the generic interface for writing log records.
// Implementations must serialize LogRecord objects and send them to
// a destination (stdout, file, UNIX socket, etc.).
type Writer interface {
	Write(rec LogRecord) error
}

// UnixSocketWriter extends Writer with a Close method for UNIX socket cleanup.
// Implementations must connect to a UNIX socket at the specified path and
// write JSON-encoded LogRecord objects. Reconnection logic should be
// implemented for transient socket failures.
type UnixSocketWriter interface {
	Writer
	Close() error
}

// MultiWriter extends Writer to support multiple output destinations.
// Implementations must write each LogRecord to all registered writers
// and provide methods to add writers and close all connections.
type MultiWriter interface {
	Writer
	Add(writer Writer)
	CloseAll() error
}

// Builder defines the interface for constructing output writers from configuration.
// Implementations must parse AppConfig.Outputs and create appropriate Writer
// instances (StdoutWriter, FileWriter, UnixSocketWriter), combining them
// into a MultiWriter if multiple outputs are configured.
type Builder interface {
	NewFromConfig(cfg AppConfig) (Writer, error)
}

// Logger defines the interface for structured service logging.
// Implementations must emit JSON-formatted log entries to stdout/stderr
// with support for multiple log levels (DEBUG, INFO, WARN, ERROR).
// Each log entry includes timestamp, level, component, message, and optional details.
type Logger interface {
	Debug(component, message string, details map[string]string)
	Info(component, message string, details map[string]string)
	Warn(component, message string, details map[string]string)
	Error(component, message string, details map[string]string)
}

// Reopenable defines the interface for components that support log file rotation.
// Implementations must reopen their output files when receiving a SIGHUP signal.
// This is used by systemctl reload to switch to new log files after logrotate.
type Reopenable interface {
	Reopen() error
}

// Helper functions for creating and converting records

// NewLogRecord creates a flattened LogRecord from TLSClientHello and Fingerprints.
// Converts TCPMeta options to a comma-separated string and creates pointer values
// for optional fields (MSS, WindowScale) to support proper JSON omitempty behavior.
// If fingerprints is nil, the JA4/JA3 fields will be empty strings.
// Note: JA4Hash is intentionally NOT included in LogRecord as the JA4 format
// already includes its own hash portions (the full 38-character JA4 string is sufficient).
func NewLogRecord(ch TLSClientHello, fp *Fingerprints) LogRecord {
	opts := ""
	if len(ch.TCPMeta.Options) > 0 {
		opts = strings.Join(ch.TCPMeta.Options, ",")
	}

	// Helper to create pointer from value for optional fields
	var mssPtr *uint16
	if ch.TCPMeta.MSS != 0 {
		mssPtr = &ch.TCPMeta.MSS
	}

	var wScalePtr *uint8
	if ch.TCPMeta.WindowScale != 0 {
		wScalePtr = &ch.TCPMeta.WindowScale
	}

	rec := LogRecord{
		SrcIP:      ch.SrcIP,
		SrcPort:    ch.SrcPort,
		DstIP:      ch.DstIP,
		DstPort:    ch.DstPort,
		IPTTL:      ch.IPMeta.TTL,
		IPTotalLen: ch.IPMeta.TotalLength,
		IPID:       ch.IPMeta.IPID,
		IPDF:       ch.IPMeta.DF,
		TCPWindow:  ch.TCPMeta.WindowSize,
		TCPMSS:     mssPtr,
		TCPWScale:  wScalePtr,
		TCPOptions: opts,
		ConnID:     ch.ConnID,
		SNI:        ch.SNI,
		ALPN:       ch.ALPN,
		TLSVersion: ch.TLSVersion,
		SynToCHMs:  ch.SynToCHMs,
		Timestamp:  time.Now().UnixNano(),
	}

	if fp != nil {
		rec.JA4 = fp.JA4
		rec.JA4T = fp.JA4T
		rec.JA3 = fp.JA3
		rec.JA3Hash = fp.JA3Hash
	}

	return rec
}

// Default values and constants

const (
	DefaultInterface    = "eth0"
	DefaultPort         = 443
	DefaultBPFFilter    = ""
	DefaultFlowTimeout  = 30 // seconds
	DefaultPacketBuffer = 1000 // packet channel buffer size
	DefaultLogLevel     = "info"
)

// DefaultConfig returns an AppConfig with sensible default values.
// Uses eth0 as the default interface, port 443 for monitoring,
// no BPF filter, a 30-second flow timeout, and a 1000-packet
// channel buffer. Returns an empty outputs slice (caller must
// configure outputs explicitly).
func DefaultConfig() AppConfig {
	return AppConfig{
		Core: Config{
			Interface:        DefaultInterface,
			ListenPorts:      []uint16{DefaultPort},
			BPFFilter:        DefaultBPFFilter,
			FlowTimeoutSec:   DefaultFlowTimeout,
			PacketBufferSize: DefaultPacketBuffer,
			LogLevel:         DefaultLogLevel,
		},
		Outputs: []OutputConfig{},
	}
}
