package matrix

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

	"github.com/google/uuid"
)

// MatrixService handles Matrix homeserver communication
type MatrixService struct {
	homeserverURL string
	accessToken   string
	serverName    string
	httpClient    *http.Client
}

// Config holds Matrix service configuration
type Config struct {
	HomeserverURL string // e.g., "http://synapse:8008"
	AccessToken   string // Admin/bot access token
	ServerName    string // e.g., "breakpilot.local"
}

// NewMatrixService creates a new Matrix service instance
func NewMatrixService(cfg Config) *MatrixService {
	return &MatrixService{
		homeserverURL: cfg.HomeserverURL,
		accessToken:   cfg.AccessToken,
		serverName:    cfg.ServerName,
		httpClient: &http.Client{
			Timeout: 30 * time.Second,
		},
	}
}

// ========================================
// Matrix API Types
// ========================================

// CreateRoomRequest represents a Matrix room creation request
type CreateRoomRequest struct {
	Name            string                 `json:"name,omitempty"`
	RoomAliasName   string                 `json:"room_alias_name,omitempty"`
	Topic           string                 `json:"topic,omitempty"`
	Visibility      string                 `json:"visibility,omitempty"` // "private" or "public"
	Preset          string                 `json:"preset,omitempty"`     // "private_chat", "public_chat", "trusted_private_chat"
	IsDirect        bool                   `json:"is_direct,omitempty"`
	Invite          []string               `json:"invite,omitempty"`
	InitialState    []StateEvent           `json:"initial_state,omitempty"`
	PowerLevelContentOverride *PowerLevels `json:"power_level_content_override,omitempty"`
}

// CreateRoomResponse represents a Matrix room creation response
type CreateRoomResponse struct {
	RoomID string `json:"room_id"`
}

// StateEvent represents a Matrix state event
type StateEvent struct {
	Type     string      `json:"type"`
	StateKey string      `json:"state_key"`
	Content  interface{} `json:"content"`
}

// PowerLevels represents Matrix power levels
type PowerLevels struct {
	Ban           int            `json:"ban,omitempty"`
	Events        map[string]int `json:"events,omitempty"`
	EventsDefault int            `json:"events_default,omitempty"`
	Invite        int            `json:"invite,omitempty"`
	Kick          int            `json:"kick,omitempty"`
	Redact        int            `json:"redact,omitempty"`
	StateDefault  int            `json:"state_default,omitempty"`
	Users         map[string]int `json:"users,omitempty"`
	UsersDefault  int            `json:"users_default,omitempty"`
}

// SendMessageRequest represents a message to send
type SendMessageRequest struct {
	MsgType       string `json:"msgtype"`
	Body          string `json:"body"`
	Format        string `json:"format,omitempty"`
	FormattedBody string `json:"formatted_body,omitempty"`
}

// UserInfo represents Matrix user information
type UserInfo struct {
	UserID      string `json:"user_id"`
	DisplayName string `json:"displayname,omitempty"`
	AvatarURL   string `json:"avatar_url,omitempty"`
}

// RegisterRequest for user registration
type RegisterRequest struct {
	Username string `json:"username"`
	Password string `json:"password,omitempty"`
	Admin    bool   `json:"admin,omitempty"`
}

// RegisterResponse for user registration
type RegisterResponse struct {
	UserID      string `json:"user_id"`
	AccessToken string `json:"access_token"`
	DeviceID    string `json:"device_id"`
}

// InviteRequest for inviting a user to a room
type InviteRequest struct {
	UserID string `json:"user_id"`
}

// JoinRequest for joining a room
type JoinRequest struct {
	Reason string `json:"reason,omitempty"`
}

// ========================================
// Room Management
// ========================================

// CreateRoom creates a new Matrix room
func (s *MatrixService) CreateRoom(ctx context.Context, req CreateRoomRequest) (*CreateRoomResponse, error) {
	body, err := json.Marshal(req)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal request: %w", err)
	}

	resp, err := s.doRequest(ctx, "POST", "/_matrix/client/v3/createRoom", body)
	if err != nil {
		return nil, fmt.Errorf("failed to create room: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, s.parseError(resp)
	}

	var result CreateRoomResponse
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	return &result, nil
}

// CreateClassInfoRoom creates a broadcast room for a class (teachers write, parents read)
func (s *MatrixService) CreateClassInfoRoom(ctx context.Context, className string, schoolName string, teacherMatrixIDs []string) (*CreateRoomResponse, error) {
	// Set up power levels: teachers can write (50), parents read-only (0)
	users := make(map[string]int)
	for _, teacherID := range teacherMatrixIDs {
		users[teacherID] = 50
	}

	req := CreateRoomRequest{
		Name:       fmt.Sprintf("%s - %s (Info)", className, schoolName),
		Topic:      fmt.Sprintf("Info-Kanal für %s. Nur Lehrer können schreiben.", className),
		Visibility: "private",
		Preset:     "private_chat",
		Invite:     teacherMatrixIDs,
		PowerLevelContentOverride: &PowerLevels{
			EventsDefault: 50, // Only power level 50+ can send messages
			UsersDefault:  0,  // Parents get power level 0 by default
			Users:         users,
			Invite:        50,
			Kick:          50,
			Ban:           50,
			Redact:        50,
		},
	}

	return s.CreateRoom(ctx, req)
}

// CreateStudentDMRoom creates a direct message room for parent-teacher communication about a student
func (s *MatrixService) CreateStudentDMRoom(ctx context.Context, studentName string, className string, teacherMatrixIDs []string, parentMatrixIDs []string) (*CreateRoomResponse, error) {
	allUsers := append(teacherMatrixIDs, parentMatrixIDs...)

	users := make(map[string]int)
	for _, id := range allUsers {
		users[id] = 50 // All can write
	}

	req := CreateRoomRequest{
		Name:       fmt.Sprintf("%s (%s) - Dialog", studentName, className),
		Topic:      fmt.Sprintf("Kommunikation über %s", studentName),
		Visibility: "private",
		Preset:     "trusted_private_chat",
		IsDirect:   false,
		Invite:     allUsers,
		InitialState: []StateEvent{
			{
				Type:     "m.room.encryption",
				StateKey: "",
				Content: map[string]string{
					"algorithm": "m.megolm.v1.aes-sha2",
				},
			},
		},
		PowerLevelContentOverride: &PowerLevels{
			EventsDefault: 0, // Everyone can send messages
			UsersDefault:  50,
			Users:         users,
		},
	}

	return s.CreateRoom(ctx, req)
}

// CreateParentRepRoom creates a room for class teacher and parent representatives
func (s *MatrixService) CreateParentRepRoom(ctx context.Context, className string, teacherMatrixIDs []string, parentRepMatrixIDs []string) (*CreateRoomResponse, error) {
	allUsers := append(teacherMatrixIDs, parentRepMatrixIDs...)

	users := make(map[string]int)
	for _, id := range allUsers {
		users[id] = 50
	}

	req := CreateRoomRequest{
		Name:       fmt.Sprintf("%s - Elternvertreter", className),
		Topic:      fmt.Sprintf("Kommunikation zwischen Lehrkräften und Elternvertretern der %s", className),
		Visibility: "private",
		Preset:     "private_chat",
		Invite:     allUsers,
		PowerLevelContentOverride: &PowerLevels{
			EventsDefault: 0,
			UsersDefault:  50,
			Users:         users,
		},
	}

	return s.CreateRoom(ctx, req)
}

// ========================================
// User Management
// ========================================

// RegisterUser registers a new Matrix user (requires admin token)
func (s *MatrixService) RegisterUser(ctx context.Context, username string, displayName string) (*RegisterResponse, error) {
	// Use admin API for user registration
	req := map[string]interface{}{
		"username": username,
		"password": uuid.New().String(), // Generate random password
		"admin":    false,
	}

	body, err := json.Marshal(req)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal request: %w", err)
	}

	// Use admin registration endpoint
	resp, err := s.doRequest(ctx, "POST", "/_synapse/admin/v2/users/@"+username+":"+s.serverName, body)
	if err != nil {
		return nil, fmt.Errorf("failed to register user: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
		return nil, s.parseError(resp)
	}

	// Set display name
	if displayName != "" {
		if err := s.SetDisplayName(ctx, "@"+username+":"+s.serverName, displayName); err != nil {
			// Log but don't fail
			fmt.Printf("Warning: failed to set display name: %v\n", err)
		}
	}

	return &RegisterResponse{
		UserID: "@" + username + ":" + s.serverName,
	}, nil
}

// SetDisplayName sets the display name for a user
func (s *MatrixService) SetDisplayName(ctx context.Context, userID string, displayName string) error {
	req := map[string]string{
		"displayname": displayName,
	}

	body, err := json.Marshal(req)
	if err != nil {
		return fmt.Errorf("failed to marshal request: %w", err)
	}

	endpoint := fmt.Sprintf("/_matrix/client/v3/profile/%s/displayname", url.PathEscape(userID))
	resp, err := s.doRequest(ctx, "PUT", endpoint, body)
	if err != nil {
		return fmt.Errorf("failed to set display name: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return s.parseError(resp)
	}

	return nil
}

// ========================================
// Room Membership
// ========================================

// InviteUser invites a user to a room
func (s *MatrixService) InviteUser(ctx context.Context, roomID string, userID string) error {
	req := InviteRequest{UserID: userID}

	body, err := json.Marshal(req)
	if err != nil {
		return fmt.Errorf("failed to marshal request: %w", err)
	}

	endpoint := fmt.Sprintf("/_matrix/client/v3/rooms/%s/invite", url.PathEscape(roomID))
	resp, err := s.doRequest(ctx, "POST", endpoint, body)
	if err != nil {
		return fmt.Errorf("failed to invite user: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return s.parseError(resp)
	}

	return nil
}

// JoinRoom makes the bot join a room
func (s *MatrixService) JoinRoom(ctx context.Context, roomIDOrAlias string) error {
	endpoint := fmt.Sprintf("/_matrix/client/v3/join/%s", url.PathEscape(roomIDOrAlias))
	resp, err := s.doRequest(ctx, "POST", endpoint, []byte("{}"))
	if err != nil {
		return fmt.Errorf("failed to join room: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return s.parseError(resp)
	}

	return nil
}

// SetUserPowerLevel sets a user's power level in a room
func (s *MatrixService) SetUserPowerLevel(ctx context.Context, roomID string, userID string, powerLevel int) error {
	// First, get current power levels
	endpoint := fmt.Sprintf("/_matrix/client/v3/rooms/%s/state/m.room.power_levels/", url.PathEscape(roomID))
	resp, err := s.doRequest(ctx, "GET", endpoint, nil)
	if err != nil {
		return fmt.Errorf("failed to get power levels: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return s.parseError(resp)
	}

	var powerLevels PowerLevels
	if err := json.NewDecoder(resp.Body).Decode(&powerLevels); err != nil {
		return fmt.Errorf("failed to decode power levels: %w", err)
	}

	// Update user power level
	if powerLevels.Users == nil {
		powerLevels.Users = make(map[string]int)
	}
	powerLevels.Users[userID] = powerLevel

	// Send updated power levels
	body, err := json.Marshal(powerLevels)
	if err != nil {
		return fmt.Errorf("failed to marshal power levels: %w", err)
	}

	resp2, err := s.doRequest(ctx, "PUT", endpoint, body)
	if err != nil {
		return fmt.Errorf("failed to set power levels: %w", err)
	}
	defer resp2.Body.Close()

	if resp2.StatusCode != http.StatusOK {
		return s.parseError(resp2)
	}

	return nil
}

