package matrix

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"

	"github.com/google/uuid"
)

// ========================================
// Messaging
// ========================================

// SendMessage sends a text message to a room
func (s *MatrixService) SendMessage(ctx context.Context, roomID string, message string) error {
	req := SendMessageRequest{
		MsgType: "m.text",
		Body:    message,
	}

	return s.sendEvent(ctx, roomID, "m.room.message", req)
}

// SendHTMLMessage sends an HTML-formatted message to a room
func (s *MatrixService) SendHTMLMessage(ctx context.Context, roomID string, plainText string, htmlBody string) error {
	req := SendMessageRequest{
		MsgType:       "m.text",
		Body:          plainText,
		Format:        "org.matrix.custom.html",
		FormattedBody: htmlBody,
	}

	return s.sendEvent(ctx, roomID, "m.room.message", req)
}

// SendAbsenceNotification sends an absence notification to parents
func (s *MatrixService) SendAbsenceNotification(ctx context.Context, roomID string, studentName string, date string, lessonNumber int) error {
	plainText := fmt.Sprintf("⚠️ Abwesenheitsmeldung\n\nIhr Kind %s war heute (%s) in der %d. Stunde nicht im Unterricht anwesend.\n\nBitte bestätigen Sie den Grund der Abwesenheit.", studentName, date, lessonNumber)

	htmlBody := fmt.Sprintf(`<h3>⚠️ Abwesenheitsmeldung</h3>
<p>Ihr Kind <strong>%s</strong> war heute (%s) in der <strong>%d. Stunde</strong> nicht im Unterricht anwesend.</p>
<p>Bitte bestätigen Sie den Grund der Abwesenheit.</p>
<ul>
<li>✅ Entschuldigt (Krankheit)</li>
<li>📋 Arztbesuch</li>
<li>❓ Sonstiges (bitte erläutern)</li>
</ul>`, studentName, date, lessonNumber)

	return s.SendHTMLMessage(ctx, roomID, plainText, htmlBody)
}

// SendGradeNotification sends a grade notification to parents
func (s *MatrixService) SendGradeNotification(ctx context.Context, roomID string, studentName string, subject string, gradeType string, grade float64) error {
	plainText := fmt.Sprintf("📊 Neue Note eingetragen\n\nFür %s wurde eine neue Note eingetragen:\n\nFach: %s\nArt: %s\nNote: %.1f", studentName, subject, gradeType, grade)

	htmlBody := fmt.Sprintf(`<h3>📊 Neue Note eingetragen</h3>
<p>Für <strong>%s</strong> wurde eine neue Note eingetragen:</p>
<table>
<tr><td>Fach:</td><td><strong>%s</strong></td></tr>
<tr><td>Art:</td><td>%s</td></tr>
<tr><td>Note:</td><td><strong>%.1f</strong></td></tr>
</table>`, studentName, subject, gradeType, grade)

	return s.SendHTMLMessage(ctx, roomID, plainText, htmlBody)
}

// SendClassAnnouncement sends an announcement to a class info room
func (s *MatrixService) SendClassAnnouncement(ctx context.Context, roomID string, title string, content string, teacherName string) error {
	plainText := fmt.Sprintf("📢 %s\n\n%s\n\n— %s", title, content, teacherName)

	htmlBody := fmt.Sprintf(`<h3>📢 %s</h3>
<p>%s</p>
<p><em>— %s</em></p>`, title, content, teacherName)

	return s.SendHTMLMessage(ctx, roomID, plainText, htmlBody)
}

// ========================================
// Internal Helpers
// ========================================

func (s *MatrixService) sendEvent(ctx context.Context, roomID string, eventType string, content interface{}) error {
	body, err := json.Marshal(content)
	if err != nil {
		return fmt.Errorf("failed to marshal content: %w", err)
	}

	txnID := uuid.New().String()
	endpoint := fmt.Sprintf("/_matrix/client/v3/rooms/%s/send/%s/%s",
		url.PathEscape(roomID), url.PathEscape(eventType), txnID)

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

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

	return nil
}

func (s *MatrixService) doRequest(ctx context.Context, method string, endpoint string, body []byte) (*http.Response, error) {
	fullURL := s.homeserverURL + endpoint

	var bodyReader io.Reader
	if body != nil {
		bodyReader = bytes.NewReader(body)
	}

	req, err := http.NewRequestWithContext(ctx, method, fullURL, bodyReader)
	if err != nil {
		return nil, err
	}

	req.Header.Set("Authorization", "Bearer "+s.accessToken)
	req.Header.Set("Content-Type", "application/json")

	return s.httpClient.Do(req)
}

func (s *MatrixService) parseError(resp *http.Response) error {
	body, _ := io.ReadAll(resp.Body)
	var errResp struct {
		ErrCode string `json:"errcode"`
		Error   string `json:"error"`
	}
	if err := json.Unmarshal(body, &errResp); err != nil {
		return fmt.Errorf("request failed with status %d: %s", resp.StatusCode, string(body))
	}
	return fmt.Errorf("matrix error %s: %s", errResp.ErrCode, errResp.Error)
}

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

// HealthCheck checks if the Matrix server is reachable
func (s *MatrixService) HealthCheck(ctx context.Context) error {
	resp, err := s.doRequest(ctx, "GET", "/_matrix/client/versions", nil)
	if err != nil {
		return fmt.Errorf("matrix server unreachable: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("matrix server returned status %d", resp.StatusCode)
	}

	return nil
}

// GetServerName returns the configured server name
func (s *MatrixService) GetServerName() string {
	return s.serverName
}

// GenerateUserID generates a Matrix user ID from a username
func (s *MatrixService) GenerateUserID(username string) string {
	return fmt.Sprintf("@%s:%s", username, s.serverName)
}
