package api

import (
	"encoding/binary"
	"encoding/json"
	"fmt"
	"sync"
	"time"
)

var bufferPool = sync.Pool{
	New: func() interface{} {
		buf := make([]byte, 0, 256)
		return &buf
	},
}

// Response packet types
const (
	PacketTypeSuccess  = 0x00
	PacketTypeError    = 0x01
	PacketTypeProgress = 0x02
	PacketTypeStatus   = 0x03
	PacketTypeData     = 0x04
	PacketTypeLog      = 0x05
)

// Error codes
const (
	ErrorCodeUnknownError          = 0x00
	ErrorCodeInvalidRequest        = 0x01
	ErrorCodeAuthenticationFailed  = 0x02
	ErrorCodePermissionDenied      = 0x03
	ErrorCodeResourceNotFound      = 0x04
	ErrorCodeResourceAlreadyExists = 0x05

	ErrorCodeServerOverloaded = 0x10
	ErrorCodeDatabaseError    = 0x11
	ErrorCodeNetworkError     = 0x12
	ErrorCodeStorageError     = 0x13
	ErrorCodeTimeout          = 0x14

	ErrorCodeJobNotFound        = 0x20
	ErrorCodeJobAlreadyRunning  = 0x21
	ErrorCodeJobFailedToStart   = 0x22
	ErrorCodeJobExecutionFailed = 0x23
	ErrorCodeJobCancelled       = 0x24

	ErrorCodeOutOfMemory          = 0x30
	ErrorCodeDiskFull             = 0x31
	ErrorCodeInvalidConfiguration = 0x32
	ErrorCodeServiceUnavailable   = 0x33
)

// Progress types
const (
	ProgressTypePercentage       = 0x00
	ProgressTypeStage            = 0x01
	ProgressTypeMessage          = 0x02
	ProgressTypeBytesTransferred = 0x03
)

// Log levels
const (
	LogLevelDebug = 0x00
	LogLevelInfo  = 0x01
	LogLevelWarn  = 0x02
	LogLevelError = 0x03
)

// ResponsePacket represents a structured response packet
type ResponsePacket struct {
	DataType        string
	SuccessMessage  string
	LogMessage      string
	ErrorMessage    string
	ErrorDetails    string
	ProgressMessage string
	StatusData      string
	DataPayload     []byte
	Timestamp       uint64
	ProgressValue   uint32
	ProgressTotal   uint32
	ErrorCode       byte
	ProgressType    byte
	LogLevel        byte
	PacketType      byte
}

// NewSuccessPacket creates a success response packet
func NewSuccessPacket(message string) *ResponsePacket {
	return &ResponsePacket{
		PacketType:     PacketTypeSuccess,
		Timestamp:      uint64(time.Now().Unix()),
		SuccessMessage: message,
	}
}

// NewSuccessPacketWithPayload creates a success response packet with JSON payload
func NewSuccessPacketWithPayload(message string, payload interface{}) *ResponsePacket {
	// Convert payload to JSON for the DataPayload field
	payloadBytes, _ := json.Marshal(payload)

	return &ResponsePacket{
		PacketType:     PacketTypeData,
		Timestamp:      uint64(time.Now().Unix()),
		SuccessMessage: message,
		DataType:       "status",
		DataPayload:    payloadBytes,
	}
}

// NewErrorPacket creates an error response packet
func NewErrorPacket(errorCode byte, message string, details string) *ResponsePacket {
	return &ResponsePacket{
		PacketType:   PacketTypeError,
		Timestamp:    uint64(time.Now().Unix()),
		ErrorCode:    errorCode,
		ErrorMessage: message,
		ErrorDetails: details,
	}
}

// NewProgressPacket creates a progress response packet
func NewProgressPacket(
	progressType byte,
	value uint32,
	total uint32,
	message string,
) *ResponsePacket {
	return &ResponsePacket{
		PacketType:      PacketTypeProgress,
		Timestamp:       uint64(time.Now().Unix()),
		ProgressType:    progressType,
		ProgressValue:   value,
		ProgressTotal:   total,
		ProgressMessage: message,
	}
}

// NewStatusPacket creates a status response packet
func NewStatusPacket(data string) *ResponsePacket {
	return &ResponsePacket{
		PacketType: PacketTypeStatus,
		Timestamp:  uint64(time.Now().Unix()),
		StatusData: data,
	}
}

// NewDataPacket creates a data response packet
func NewDataPacket(dataType string, payload []byte) *ResponsePacket {
	return &ResponsePacket{
		PacketType:  PacketTypeData,
		Timestamp:   uint64(time.Now().Unix()),
		DataType:    dataType,
		DataPayload: payload,
	}
}

// NewLogPacket creates a log response packet
func NewLogPacket(level byte, message string) *ResponsePacket {
	return &ResponsePacket{
		PacketType: PacketTypeLog,
		Timestamp:  uint64(time.Now().Unix()),
		LogLevel:   level,
		LogMessage: message,
	}
}

// Serialize converts the packet to binary format
func (p *ResponsePacket) Serialize() ([]byte, error) {
	// For small packets, avoid pool overhead
	if p.estimatedSize() <= 1024 {
		buf := make([]byte, 0, p.estimatedSize())
		return serializePacketToBuffer(p, buf)
	}

	// Use pool for larger packets
	bufPtr := bufferPool.Get().(*[]byte)
	defer func() {
		*bufPtr = (*bufPtr)[:0]
		bufferPool.Put(bufPtr)
	}()
	buf := *bufPtr

	// Ensure buffer has enough capacity
	if cap(buf) < p.estimatedSize() {
		buf = make([]byte, 0, p.estimatedSize())
	} else {
		buf = buf[:0]
	}

	return serializePacketToBuffer(p, buf)
}

func serializePacketToBuffer(p *ResponsePacket, buf []byte) ([]byte, error) {
	// Packet type
	buf = append(buf, p.PacketType)

	// Timestamp (8 bytes, big-endian)
	var timestampBytes [8]byte
	binary.BigEndian.PutUint64(timestampBytes[:], p.Timestamp)
	buf = append(buf, timestampBytes[:]...)

	// Packet-specific data
	switch p.PacketType {
	case PacketTypeSuccess:
		buf = appendString(buf, p.SuccessMessage)

	case PacketTypeError:
		buf = append(buf, p.ErrorCode)
		buf = appendString(buf, p.ErrorMessage)
		buf = appendString(buf, p.ErrorDetails)

	case PacketTypeProgress:
		buf = append(buf, p.ProgressType)
		buf = appendUint32(buf, p.ProgressValue)
		buf = appendUint32(buf, p.ProgressTotal)
		buf = appendString(buf, p.ProgressMessage)

	case PacketTypeStatus:
		buf = appendString(buf, p.StatusData)

	case PacketTypeData:
		buf = appendString(buf, p.DataType)
		buf = appendBytes(buf, p.DataPayload)

	case PacketTypeLog:
		buf = append(buf, p.LogLevel)
		buf = appendString(buf, p.LogMessage)

	default:
		return nil, fmt.Errorf("unknown packet type: %d", p.PacketType)
	}

	return buf, nil
}

// appendString writes a string with fixed 16-bit length prefix
func appendString(buf []byte, s string) []byte {
	length := uint16(len(s))
	buf = append(buf, byte(length>>8), byte(length))
	buf = append(buf, s...)
	return buf
}

// appendBytes writes bytes with fixed 32-bit length prefix
func appendBytes(buf []byte, b []byte) []byte {
	length := uint32(len(b))
	buf = append(buf, byte(length>>24), byte(length>>16), byte(length>>8), byte(length))
	buf = append(buf, b...)
	return buf
}

func appendUint32(buf []byte, value uint32) []byte {
	var tmp [4]byte
	binary.BigEndian.PutUint32(tmp[:], value)
	return append(buf, tmp[:]...)
}

func (p *ResponsePacket) estimatedSize() int {
	base := 1 + 8 // packet type + timestamp

	switch p.PacketType {
	case PacketTypeSuccess:
		return base + 2 + len(p.SuccessMessage)
	case PacketTypeError:
		return base + 1 + 2 + len(p.ErrorMessage) + 2 + len(p.ErrorDetails)
	case PacketTypeProgress:
		return base + 1 + 4 + 4 + 2 + len(p.ProgressMessage)
	case PacketTypeStatus:
		return base + 2 + len(p.StatusData)
	case PacketTypeData:
		return base + 2 + len(p.DataType) + 4 + len(p.DataPayload)
	case PacketTypeLog:
		return base + 1 + 2 + len(p.LogMessage)
	default:
		return base
	}
}

// GetErrorMessage returns a human-readable error message for an error code
func GetErrorMessage(code byte) string {
	switch code {
	case ErrorCodeUnknownError:
		return "Unknown error occurred"
	case ErrorCodeInvalidRequest:
		return "Invalid request format"
	case ErrorCodeAuthenticationFailed:
		return "Authentication failed"
	case ErrorCodePermissionDenied:
		return "Permission denied"
	case ErrorCodeResourceNotFound:
		return "Resource not found"
	case ErrorCodeResourceAlreadyExists:
		return "Resource already exists"
	case ErrorCodeServerOverloaded:
		return "Server is overloaded"
	case ErrorCodeDatabaseError:
		return "Database error occurred"
	case ErrorCodeNetworkError:
		return "Network error occurred"
	case ErrorCodeStorageError:
		return "Storage error occurred"
	case ErrorCodeTimeout:
		return "Operation timed out"
	case ErrorCodeJobNotFound:
		return "Job not found"
	case ErrorCodeJobAlreadyRunning:
		return "Job is already running"
	case ErrorCodeJobFailedToStart:
		return "Job failed to start"
	case ErrorCodeJobExecutionFailed:
		return "Job execution failed"
	case ErrorCodeJobCancelled:
		return "Job was cancelled"
	case ErrorCodeOutOfMemory:
		return "Server out of memory"
	case ErrorCodeDiskFull:
		return "Server disk full"
	case ErrorCodeInvalidConfiguration:
		return "Invalid server configuration"
	case ErrorCodeServiceUnavailable:
		return "Service temporarily unavailable"
	default:
		return "Unknown error code"
	}
}

// GetLogLevelName returns the name for a log level
func GetLogLevelName(level byte) string {
	switch level {
	case LogLevelDebug:
		return "DEBUG"
	case LogLevelInfo:
		return "INFO"
	case LogLevelWarn:
		return "WARN"
	case LogLevelError:
		return "ERROR"
	default:
		return "UNKNOWN"
	}
}
