package server

import (
	"encoding/xml"
	"fmt"
	"sync"
	"time"
)

// PTZ service SOAP message types

// ContinuousMoveRequest represents ContinuousMove request
type ContinuousMoveRequest struct {
	XMLName      xml.Name  `xml:"http://www.onvif.org/ver20/ptz/wsdl ContinuousMove"`
	ProfileToken string    `xml:"ProfileToken"`
	Velocity     PTZVector `xml:"Velocity"`
	Timeout      string    `xml:"Timeout,omitempty"`
}

// ContinuousMoveResponse represents ContinuousMove response
type ContinuousMoveResponse struct {
	XMLName xml.Name `xml:"http://www.onvif.org/ver20/ptz/wsdl ContinuousMoveResponse"`
}

// AbsoluteMoveRequest represents AbsoluteMove request
type AbsoluteMoveRequest struct {
	XMLName      xml.Name  `xml:"http://www.onvif.org/ver20/ptz/wsdl AbsoluteMove"`
	ProfileToken string    `xml:"ProfileToken"`
	Position     PTZVector `xml:"Position"`
	Speed        PTZVector `xml:"Speed,omitempty"`
}

// AbsoluteMoveResponse represents AbsoluteMove response
type AbsoluteMoveResponse struct {
	XMLName xml.Name `xml:"http://www.onvif.org/ver20/ptz/wsdl AbsoluteMoveResponse"`
}

// RelativeMoveRequest represents RelativeMove request
type RelativeMoveRequest struct {
	XMLName      xml.Name  `xml:"http://www.onvif.org/ver20/ptz/wsdl RelativeMove"`
	ProfileToken string    `xml:"ProfileToken"`
	Translation  PTZVector `xml:"Translation"`
	Speed        PTZVector `xml:"Speed,omitempty"`
}

// RelativeMoveResponse represents RelativeMove response
type RelativeMoveResponse struct {
	XMLName xml.Name `xml:"http://www.onvif.org/ver20/ptz/wsdl RelativeMoveResponse"`
}

// StopRequest represents Stop request
type StopRequest struct {
	XMLName      xml.Name `xml:"http://www.onvif.org/ver20/ptz/wsdl Stop"`
	ProfileToken string   `xml:"ProfileToken"`
	PanTilt      bool     `xml:"PanTilt,omitempty"`
	Zoom         bool     `xml:"Zoom,omitempty"`
}

// StopResponse represents Stop response
type StopResponse struct {
	XMLName xml.Name `xml:"http://www.onvif.org/ver20/ptz/wsdl StopResponse"`
}

// GetStatusRequest represents GetStatus request
type GetStatusRequest struct {
	XMLName      xml.Name `xml:"http://www.onvif.org/ver20/ptz/wsdl GetStatus"`
	ProfileToken string   `xml:"ProfileToken"`
}

// GetStatusResponse represents GetStatus response
type GetStatusResponse struct {
	XMLName   xml.Name   `xml:"http://www.onvif.org/ver20/ptz/wsdl GetStatusResponse"`
	PTZStatus *PTZStatus `xml:"PTZStatus"`
}

// PTZStatus represents PTZ status
type PTZStatus struct {
	Position   PTZVector      `xml:"Position"`
	MoveStatus PTZMoveStatus  `xml:"MoveStatus"`
	UTCTime    string         `xml:"UtcTime"`
}

// PTZMoveStatus represents PTZ movement status
type PTZMoveStatus struct {
	PanTilt string `xml:"PanTilt,omitempty"`
	Zoom    string `xml:"Zoom,omitempty"`
}

// PTZVector represents PTZ position/velocity
type PTZVector struct {
	PanTilt *Vector2D `xml:"PanTilt,omitempty"`
	Zoom    *Vector1D `xml:"Zoom,omitempty"`
}

// Vector2D represents a 2D vector
type Vector2D struct {
	X     float64 `xml:"x,attr"`
	Y     float64 `xml:"y,attr"`
	Space string  `xml:"space,attr,omitempty"`
}

// Vector1D represents a 1D vector
type Vector1D struct {
	X     float64 `xml:"x,attr"`
	Space string  `xml:"space,attr,omitempty"`
}

// GetPresetsRequest represents GetPresets request
type GetPresetsRequest struct {
	XMLName      xml.Name `xml:"http://www.onvif.org/ver20/ptz/wsdl GetPresets"`
	ProfileToken string   `xml:"ProfileToken"`
}

// GetPresetsResponse represents GetPresets response
type GetPresetsResponse struct {
	XMLName xml.Name     `xml:"http://www.onvif.org/ver20/ptz/wsdl GetPresetsResponse"`
	Preset  []PTZPreset `xml:"Preset"`
}

// PTZPreset represents a PTZ preset
type PTZPreset struct {
	Token       string     `xml:"token,attr"`
	Name        string     `xml:"Name"`
	PTZPosition *PTZVector `xml:"PTZPosition,omitempty"`
}

// GotoPresetRequest represents GotoPreset request
type GotoPresetRequest struct {
	XMLName      xml.Name  `xml:"http://www.onvif.org/ver20/ptz/wsdl GotoPreset"`
	ProfileToken string    `xml:"ProfileToken"`
	PresetToken  string    `xml:"PresetToken"`
	Speed        PTZVector `xml:"Speed,omitempty"`
}

// GotoPresetResponse represents GotoPreset response
type GotoPresetResponse struct {
	XMLName xml.Name `xml:"http://www.onvif.org/ver20/ptz/wsdl GotoPresetResponse"`
}

// SetPresetRequest represents SetPreset request
type SetPresetRequest struct {
	XMLName      xml.Name `xml:"http://www.onvif.org/ver20/ptz/wsdl SetPreset"`
	ProfileToken string   `xml:"ProfileToken"`
	PresetName   string   `xml:"PresetName,omitempty"`
	PresetToken  string   `xml:"PresetToken,omitempty"`
}

// SetPresetResponse represents SetPreset response
type SetPresetResponse struct {
	XMLName     xml.Name `xml:"http://www.onvif.org/ver20/ptz/wsdl SetPresetResponse"`
	PresetToken string   `xml:"PresetToken"`
}

// GetConfigurationsResponse represents GetConfigurations response
type GetConfigurationsResponse struct {
	XMLName        xml.Name           `xml:"http://www.onvif.org/ver20/ptz/wsdl GetConfigurationsResponse"`
	PTZConfiguration []PTZConfigurationExt `xml:"PTZConfiguration"`
}

// PTZConfigurationExt represents PTZ configuration with extensions
type PTZConfigurationExt struct {
	Token      string       `xml:"token,attr"`
	Name       string       `xml:"Name"`
	UseCount   int          `xml:"UseCount"`
	NodeToken  string       `xml:"NodeToken"`
	PanTiltLimits *PanTiltLimits `xml:"PanTiltLimits,omitempty"`
	ZoomLimits    *ZoomLimits    `xml:"ZoomLimits,omitempty"`
}

// PanTiltLimits represents pan/tilt limits
type PanTiltLimits struct {
	Range Space2DDescription `xml:"Range"`
}

// ZoomLimits represents zoom limits
type ZoomLimits struct {
	Range Space1DDescription `xml:"Range"`
}

// Space2DDescription represents 2D space description
type Space2DDescription struct {
	URI    string     `xml:"URI"`
	XRange FloatRange `xml:"XRange"`
	YRange FloatRange `xml:"YRange"`
}

// Space1DDescription represents 1D space description
type Space1DDescription struct {
	URI    string     `xml:"URI"`
	XRange FloatRange `xml:"XRange"`
}

// FloatRange represents a float range
type FloatRange struct {
	Min float64 `xml:"Min"`
	Max float64 `xml:"Max"`
}

// PTZ service handlers

var ptzMutex sync.RWMutex

// HandleContinuousMove handles ContinuousMove request
func (s *Server) HandleContinuousMove(body interface{}) (interface{}, error) {
	var req ContinuousMoveRequest
	if err := unmarshalBody(body, &req); err != nil {
		return nil, fmt.Errorf("invalid request: %w", err)
	}

	// Get PTZ state
	ptzMutex.Lock()
	defer ptzMutex.Unlock()

	state, ok := s.ptzState[req.ProfileToken]
	if !ok {
		return nil, fmt.Errorf("PTZ not supported for profile: %s", req.ProfileToken)
	}

	// Set movement state
	state.Moving = true
	if req.Velocity.PanTilt != nil {
		state.PanMoving = req.Velocity.PanTilt.X != 0 || req.Velocity.PanTilt.Y != 0
		state.TiltMoving = state.PanMoving
	}
	if req.Velocity.Zoom != nil {
		state.ZoomMoving = req.Velocity.Zoom.X != 0
	}
	state.LastUpdate = time.Now()

	// In a real implementation, this would start a background task to
	// simulate movement and update position over time

	return &ContinuousMoveResponse{}, nil
}

// HandleAbsoluteMove handles AbsoluteMove request
func (s *Server) HandleAbsoluteMove(body interface{}) (interface{}, error) {
	var req AbsoluteMoveRequest
	if err := unmarshalBody(body, &req); err != nil {
		return nil, fmt.Errorf("invalid request: %w", err)
	}

	// Get PTZ state
	ptzMutex.Lock()
	defer ptzMutex.Unlock()

	state, ok := s.ptzState[req.ProfileToken]
	if !ok {
		return nil, fmt.Errorf("PTZ not supported for profile: %s", req.ProfileToken)
	}

	// Update position
	if req.Position.PanTilt != nil {
		state.Position.Pan = req.Position.PanTilt.X
		state.Position.Tilt = req.Position.PanTilt.Y
	}
	if req.Position.Zoom != nil {
		state.Position.Zoom = req.Position.Zoom.X
	}

	// Set moving state temporarily
	state.Moving = true
	state.PanMoving = req.Position.PanTilt != nil
	state.TiltMoving = req.Position.PanTilt != nil
	state.ZoomMoving = req.Position.Zoom != nil
	state.LastUpdate = time.Now()

	// In a real implementation, simulate movement over time
	// For now, we'll stop immediately
	go func() {
		time.Sleep(500 * time.Millisecond)
		ptzMutex.Lock()
		state.Moving = false
		state.PanMoving = false
		state.TiltMoving = false
		state.ZoomMoving = false
		ptzMutex.Unlock()
	}()

	return &AbsoluteMoveResponse{}, nil
}

// HandleRelativeMove handles RelativeMove request
func (s *Server) HandleRelativeMove(body interface{}) (interface{}, error) {
	var req RelativeMoveRequest
	if err := unmarshalBody(body, &req); err != nil {
		return nil, fmt.Errorf("invalid request: %w", err)
	}

	// Get PTZ state
	ptzMutex.Lock()
	defer ptzMutex.Unlock()

	state, ok := s.ptzState[req.ProfileToken]
	if !ok {
		return nil, fmt.Errorf("PTZ not supported for profile: %s", req.ProfileToken)
	}

	// Update position relatively
	if req.Translation.PanTilt != nil {
		state.Position.Pan += req.Translation.PanTilt.X
		state.Position.Tilt += req.Translation.PanTilt.Y
	}
	if req.Translation.Zoom != nil {
		state.Position.Zoom += req.Translation.Zoom.X
	}

	// Clamp values to valid ranges (simplified)
	state.Position.Pan = clamp(state.Position.Pan, -180, 180)
	state.Position.Tilt = clamp(state.Position.Tilt, -90, 90)
	state.Position.Zoom = clamp(state.Position.Zoom, 0, 1)

	state.Moving = true
	state.LastUpdate = time.Now()

	// Simulate movement completion
	go func() {
		time.Sleep(500 * time.Millisecond)
		ptzMutex.Lock()
		state.Moving = false
		state.PanMoving = false
		state.TiltMoving = false
		state.ZoomMoving = false
		ptzMutex.Unlock()
	}()

	return &RelativeMoveResponse{}, nil
}

// HandleStop handles Stop request
func (s *Server) HandleStop(body interface{}) (interface{}, error) {
	var req StopRequest
	if err := unmarshalBody(body, &req); err != nil {
		return nil, fmt.Errorf("invalid request: %w", err)
	}

	// Get PTZ state
	ptzMutex.Lock()
	defer ptzMutex.Unlock()

	state, ok := s.ptzState[req.ProfileToken]
	if !ok {
		return nil, fmt.Errorf("PTZ not supported for profile: %s", req.ProfileToken)
	}

	// Stop movement
	if req.PanTilt {
		state.PanMoving = false
		state.TiltMoving = false
	}
	if req.Zoom {
		state.ZoomMoving = false
	}
	if !req.PanTilt && !req.Zoom {
		// Stop all if neither specified
		state.PanMoving = false
		state.TiltMoving = false
		state.ZoomMoving = false
	}
	state.Moving = state.PanMoving || state.TiltMoving || state.ZoomMoving
	state.LastUpdate = time.Now()

	return &StopResponse{}, nil
}

// HandleGetStatus handles GetStatus request
func (s *Server) HandleGetStatus(body interface{}) (interface{}, error) {
	var req GetStatusRequest
	if err := unmarshalBody(body, &req); err != nil {
		return nil, fmt.Errorf("invalid request: %w", err)
	}

	// Get PTZ state
	ptzMutex.RLock()
	defer ptzMutex.RUnlock()

	state, ok := s.ptzState[req.ProfileToken]
	if !ok {
		return nil, fmt.Errorf("PTZ not supported for profile: %s", req.ProfileToken)
	}

	// Build status response
	status := &PTZStatus{
		Position: PTZVector{
			PanTilt: &Vector2D{
				X:     state.Position.Pan,
				Y:     state.Position.Tilt,
				Space: "http://www.onvif.org/ver10/tptz/PanTiltSpaces/PositionGenericSpace",
			},
			Zoom: &Vector1D{
				X:     state.Position.Zoom,
				Space: "http://www.onvif.org/ver10/tptz/ZoomSpaces/PositionGenericSpace",
			},
		},
		MoveStatus: PTZMoveStatus{
			PanTilt: getMoveStatusString(state.PanMoving || state.TiltMoving),
			Zoom:    getMoveStatusString(state.ZoomMoving),
		},
		UTCTime: time.Now().UTC().Format(time.RFC3339),
	}

	return &GetStatusResponse{
		PTZStatus: status,
	}, nil
}

// HandleGetPresets handles GetPresets request
func (s *Server) HandleGetPresets(body interface{}) (interface{}, error) {
	var req GetPresetsRequest
	if err := unmarshalBody(body, &req); err != nil {
		return nil, fmt.Errorf("invalid request: %w", err)
	}

	// Find the profile configuration
	var profileCfg *ProfileConfig
	for i := range s.config.Profiles {
		if s.config.Profiles[i].Token == req.ProfileToken {
			profileCfg = &s.config.Profiles[i]
			break
		}
	}

	if profileCfg == nil || profileCfg.PTZ == nil {
		return nil, fmt.Errorf("PTZ not supported for profile: %s", req.ProfileToken)
	}

	// Build presets response
	presets := make([]PTZPreset, len(profileCfg.PTZ.Presets))
	for i, preset := range profileCfg.PTZ.Presets {
		presets[i] = PTZPreset{
			Token: preset.Token,
			Name:  preset.Name,
			PTZPosition: &PTZVector{
				PanTilt: &Vector2D{
					X: preset.Position.Pan,
					Y: preset.Position.Tilt,
				},
				Zoom: &Vector1D{
					X: preset.Position.Zoom,
				},
			},
		}
	}

	return &GetPresetsResponse{
		Preset: presets,
	}, nil
}

// HandleGotoPreset handles GotoPreset request
func (s *Server) HandleGotoPreset(body interface{}) (interface{}, error) {
	var req GotoPresetRequest
	if err := unmarshalBody(body, &req); err != nil {
		return nil, fmt.Errorf("invalid request: %w", err)
	}

	// Find the profile configuration
	var profileCfg *ProfileConfig
	for i := range s.config.Profiles {
		if s.config.Profiles[i].Token == req.ProfileToken {
			profileCfg = &s.config.Profiles[i]
			break
		}
	}

	if profileCfg == nil || profileCfg.PTZ == nil {
		return nil, fmt.Errorf("PTZ not supported for profile: %s", req.ProfileToken)
	}

	// Find the preset
	var presetPos *PTZPosition
	for _, preset := range profileCfg.PTZ.Presets {
		if preset.Token == req.PresetToken {
			presetPos = &preset.Position
			break
		}
	}

	if presetPos == nil {
		return nil, fmt.Errorf("preset not found: %s", req.PresetToken)
	}

	// Get PTZ state and move to preset
	ptzMutex.Lock()
	defer ptzMutex.Unlock()

	state := s.ptzState[req.ProfileToken]
	state.Position = *presetPos
	state.Moving = true
	state.PanMoving = true
	state.TiltMoving = true
	state.ZoomMoving = true
	state.LastUpdate = time.Now()

	// Simulate movement completion
	go func() {
		time.Sleep(1 * time.Second)
		ptzMutex.Lock()
		state.Moving = false
		state.PanMoving = false
		state.TiltMoving = false
		state.ZoomMoving = false
		ptzMutex.Unlock()
	}()

	return &GotoPresetResponse{}, nil
}

// Helper functions

func getMoveStatusString(moving bool) string {
	if moving {
		return "MOVING"
	}
	return "IDLE"
}

func clamp(value, min, max float64) float64 {
	if value < min {
		return min
	}
	if value > max {
		return max
	}
	return value
}
