package jitsi

import (
	"context"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strings"
	"time"

	"crypto/hmac"
	"crypto/sha256"

	"github.com/google/uuid"
)

// ========================================
// JWT Generation
// ========================================

// generateJWT creates a signed JWT for Jitsi authentication
func (s *JitsiService) generateJWT(meeting Meeting, roomName string) (string, *time.Time, error) {
	if s.appSecret == "" {
		return "", nil, fmt.Errorf("app secret not configured")
	}

	now := time.Now()

	// Default expiration: 24 hours or based on meeting duration
	expiration := now.Add(24 * time.Hour)
	if meeting.Duration > 0 {
		expiration = now.Add(time.Duration(meeting.Duration+30) * time.Minute)
	}
	if meeting.StartTime != nil {
		expiration = meeting.StartTime.Add(time.Duration(meeting.Duration+60) * time.Minute)
	}

	claims := JWTClaims{
		Audience:  "jitsi",
		Issuer:    s.appID,
		Subject:   "meet.jitsi",
		Room:      roomName,
		ExpiresAt: expiration.Unix(),
		NotBefore: now.Add(-5 * time.Minute).Unix(), // 5 min grace period
		Moderator: meeting.Moderator,
		Context: &JWTContext{
			User: &JWTUser{
				ID:        uuid.New().String(),
				Name:      meeting.DisplayName,
				Email:     meeting.Email,
				Avatar:    meeting.Avatar,
				Moderator: meeting.Moderator,
			},
		},
	}

	// Add features if specified
	if meeting.Features != nil {
		claims.Features = &JWTFeatures{
			Recording:     boolToString(meeting.Features.Recording),
			Livestreaming: boolToString(meeting.Features.Livestreaming),
			Transcription: boolToString(meeting.Features.Transcription),
			OutboundCall:  boolToString(meeting.Features.OutboundCall),
		}
	}

	// Create JWT
	token, err := s.signJWT(claims)
	if err != nil {
		return "", nil, err
	}

	return token, &expiration, nil
}

// signJWT creates and signs a JWT token
func (s *JitsiService) signJWT(claims JWTClaims) (string, error) {
	// Header
	header := map[string]string{
		"alg": "HS256",
		"typ": "JWT",
	}

	headerJSON, err := json.Marshal(header)
	if err != nil {
		return "", err
	}

	// Payload
	payloadJSON, err := json.Marshal(claims)
	if err != nil {
		return "", err
	}

	// Encode
	headerB64 := base64.RawURLEncoding.EncodeToString(headerJSON)
	payloadB64 := base64.RawURLEncoding.EncodeToString(payloadJSON)

	// Sign
	message := headerB64 + "." + payloadB64
	h := hmac.New(sha256.New, []byte(s.appSecret))
	h.Write([]byte(message))
	signature := base64.RawURLEncoding.EncodeToString(h.Sum(nil))

	return message + "." + signature, nil
}

// ========================================
// Health Check
// ========================================

// HealthCheck verifies the Jitsi server is accessible
func (s *JitsiService) HealthCheck(ctx context.Context) error {
	req, err := http.NewRequestWithContext(ctx, "GET", s.baseURL, nil)
	if err != nil {
		return fmt.Errorf("failed to create request: %w", err)
	}

	resp, err := s.httpClient.Do(req)
	if err != nil {
		return fmt.Errorf("jitsi server unreachable: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 500 {
		return fmt.Errorf("jitsi server error: status %d", resp.StatusCode)
	}

	return nil
}

// GetServerInfo returns information about the Jitsi server
func (s *JitsiService) GetServerInfo() map[string]string {
	return map[string]string{
		"base_url":     s.baseURL,
		"app_id":       s.appID,
		"auth_enabled": boolToString(s.appSecret != ""),
	}
}

// ========================================
// URL Building
// ========================================

// BuildEmbedURL creates an embeddable iframe URL
func (s *JitsiService) BuildEmbedURL(roomName string, displayName string, config *MeetingConfig) string {
	params := url.Values{}

	if displayName != "" {
		params.Set("userInfo.displayName", displayName)
	}

	if config != nil {
		if config.StartWithAudioMuted {
			params.Set("config.startWithAudioMuted", "true")
		}
		if config.StartWithVideoMuted {
			params.Set("config.startWithVideoMuted", "true")
		}
		if config.DisableDeepLinking {
			params.Set("config.disableDeepLinking", "true")
		}
	}

	embedURL := fmt.Sprintf("%s/%s", s.baseURL, s.sanitizeRoomName(roomName))
	if len(params) > 0 {
		embedURL += "#" + params.Encode()
	}

	return embedURL
}

// BuildIFrameCode generates HTML iframe code for embedding
func (s *JitsiService) BuildIFrameCode(roomName string, width int, height int) string {
	if width == 0 {
		width = 800
	}
	if height == 0 {
		height = 600
	}

	return fmt.Sprintf(
		`<iframe src="%s/%s" width="%d" height="%d" allow="camera; microphone; fullscreen; display-capture; autoplay" style="border: 0;"></iframe>`,
		s.baseURL,
		s.sanitizeRoomName(roomName),
		width,
		height,
	)
}

// ========================================
// Helper Functions
// ========================================

// generateRoomName creates a unique room name
func (s *JitsiService) generateRoomName() string {
	return fmt.Sprintf("breakpilot-%s", uuid.New().String()[:8])
}

// generateTrainingRoomName creates a room name for training sessions
func (s *JitsiService) generateTrainingRoomName(title string) string {
	sanitized := s.sanitizeRoomName(title)
	if sanitized == "" {
		sanitized = "schulung"
	}
	return fmt.Sprintf("%s-%s", sanitized, time.Now().Format("20060102-1504"))
}

// sanitizeRoomName removes invalid characters from room names
func (s *JitsiService) sanitizeRoomName(name string) string {
	// Replace spaces and special characters
	result := strings.ToLower(name)
	result = strings.ReplaceAll(result, " ", "-")
	result = strings.ReplaceAll(result, "ä", "ae")
	result = strings.ReplaceAll(result, "ö", "oe")
	result = strings.ReplaceAll(result, "ü", "ue")
	result = strings.ReplaceAll(result, "ß", "ss")

	// Remove any remaining non-alphanumeric characters except hyphen
	var cleaned strings.Builder
	for _, r := range result {
		if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '-' {
			cleaned.WriteRune(r)
		}
	}

	// Remove consecutive hyphens
	result = cleaned.String()
	for strings.Contains(result, "--") {
		result = strings.ReplaceAll(result, "--", "-")
	}

	// Trim hyphens from start and end
	result = strings.Trim(result, "-")

	// Limit length
	if len(result) > 50 {
		result = result[:50]
	}

	return result
}

// generatePassword creates a random meeting password
func (s *JitsiService) generatePassword() string {
	return uuid.New().String()[:8]
}

// buildConfigParams creates URL parameters from config
func (s *JitsiService) buildConfigParams(config *MeetingConfig) string {
	params := url.Values{}

	if config.StartWithAudioMuted {
		params.Set("config.startWithAudioMuted", "true")
	}
	if config.StartWithVideoMuted {
		params.Set("config.startWithVideoMuted", "true")
	}
	if config.DisableDeepLinking {
		params.Set("config.disableDeepLinking", "true")
	}
	if config.RequireDisplayName {
		params.Set("config.requireDisplayName", "true")
	}
	if config.EnableLobby {
		params.Set("config.enableLobby", "true")
	}

	return params.Encode()
}

// boolToString converts bool to "true"/"false" string
func boolToString(b bool) string {
	if b {
		return "true"
	}
	return "false"
}

// GetBaseURL returns the configured base URL
func (s *JitsiService) GetBaseURL() string {
	return s.baseURL
}

// IsAuthEnabled returns whether JWT authentication is configured
func (s *JitsiService) IsAuthEnabled() bool {
	return s.appSecret != ""
}
