package roomapi

import (
	"fmt"
	"html"
	"net/http"
	"net/url"
	"strings"
	"time"

	"hyapp/pkg/roomid"
	"hyapp/services/gateway-service/internal/auth"
	"hyapp/services/gateway-service/internal/transport/http/httpkit"

	roomv1 "hyapp.local/api/proto/room/v1"
	userv1 "hyapp.local/api/proto/user/v1"
)

const (
	roomShareLandingPathPrefix = "/share/rooms/"
	roomShareAppScheme         = "laluparty"
	roomShareAppName           = "Lalu Party"
	roomShareAndroidPackage    = "com.org.laluparty"
)

// getRoomShare 返回 App 侧调起系统分享面板需要的稳定资料。
// 分享本身不改变房间状态，所以这里只读 room-service 快照和 user-service 邀请码。
func (h *Handler) getRoomShare(writer http.ResponseWriter, request *http.Request) {
	if h.roomQueryClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}
	roomID := strings.TrimSpace(request.PathValue("room_id"))
	if !roomid.ValidStringID(roomID) {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}

	viewerUserID := auth.UserIDFromContext(request.Context())
	snapshotResp, err := h.roomQueryClient.GetRoomSnapshot(request.Context(), &roomv1.GetRoomSnapshotRequest{
		Meta:         httpkit.RoomMeta(request, roomID, ""),
		RoomId:       roomID,
		ViewerUserId: viewerUserID,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}

	inviteCode, err := h.roomShareInviteCode(request, viewerUserID)
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	httpkit.WriteOK(writer, request, h.roomShareData(request, snapshotResp.GetRoom(), inviteCode))
}

// getRoomShareLanding 返回第三方平台爬取的分享落地页。
// 这个入口不要求登录，目的是让 WhatsApp/Facebook/X 先拿到 OG/Twitter meta；真实进房仍由 App 登录和 JoinRoom 校验。
func (h *Handler) getRoomShareLanding(writer http.ResponseWriter, request *http.Request) {
	if h.roomQueryClient == nil {
		http.Error(writer, "upstream service error", http.StatusBadGateway)
		return
	}
	roomID := strings.TrimSpace(request.PathValue("room_id"))
	if !roomid.ValidStringID(roomID) {
		http.Error(writer, "invalid room", http.StatusBadRequest)
		return
	}

	snapshotResp, err := h.roomQueryClient.GetRoomSnapshot(request.Context(), &roomv1.GetRoomSnapshotRequest{
		Meta:   httpkit.RoomMeta(request, roomID, ""),
		RoomId: roomID,
	})
	if err != nil {
		http.Error(writer, "room unavailable", http.StatusBadGateway)
		return
	}

	data := h.roomShareData(request, snapshotResp.GetRoom(), strings.TrimSpace(request.URL.Query().Get("invite_code")))
	writer.Header().Set("Content-Type", "text/html; charset=utf-8")
	writer.Header().Set("X-Content-Type-Options", "nosniff")
	_, _ = writer.Write([]byte(roomShareLandingHTML(data)))
}

func (h *Handler) roomShareData(request *http.Request, snapshot *roomv1.RoomSnapshot, inviteCode string) roomShareData {
	owner := h.roomShareOwner(request, snapshot)
	title := roomShareTitle(snapshot)
	description := roomShareDescription(snapshot, owner)
	landingURL := roomShareLandingURL(request, snapshot.GetRoomId(), inviteCode)
	deepLink := roomShareDeepLink(snapshot.GetRoomId(), inviteCode)
	coverURL := roomShareCoverURL(snapshot, owner)

	return roomShareData{
		RoomID:       snapshot.GetRoomId(),
		RoomShortID:  strings.TrimSpace(snapshot.GetRoomShortId()),
		Title:        title,
		Description:  description,
		CoverURL:     coverURL,
		LandingURL:   landingURL,
		DeepLink:     deepLink,
		ShareText:    roomShareText(title, owner),
		InviteCode:   strings.TrimSpace(inviteCode),
		Owner:        owner,
		ServerTimeMS: time.Now().UTC().UnixMilli(),
		Preview: roomSharePreviewData{
			Type:        "website",
			Title:       title,
			Description: description,
			ImageURL:    coverURL,
			URL:         landingURL,
		},
	}
}

func (h *Handler) roomShareInviteCode(request *http.Request, userID int64) (string, error) {
	if userID <= 0 || h.userProfileClient == nil {
		return "", nil
	}
	userResp, err := h.userProfileClient.GetUser(request.Context(), &userv1.GetUserRequest{
		Meta:   httpkit.UserMeta(request, ""),
		UserId: userID,
	})
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(userResp.GetUser().GetInvite().GetMyInviteCode()), nil
}

func (h *Handler) roomShareOwner(request *http.Request, snapshot *roomv1.RoomSnapshot) roomShareOwnerData {
	ownerUserID := snapshot.GetOwnerUserId()
	owner := roomShareOwnerData{UserID: formatOptionalUserID(ownerUserID)}
	if ownerUserID <= 0 || h.userProfileClient == nil {
		return owner
	}
	if profile, ok := h.roomDisplayProfileMap(request, []int64{ownerUserID})[ownerUserID]; ok {
		owner.DisplayUserID = strings.TrimSpace(profile.DisplayUserID)
		owner.Username = strings.TrimSpace(profile.Username)
		owner.Avatar = strings.TrimSpace(profile.Avatar)
		owner.CountryFlag = strings.TrimSpace(profile.CountryFlag)
	}
	return owner
}

func roomShareTitle(snapshot *roomv1.RoomSnapshot) string {
	if snapshot == nil {
		return roomShareAppName + " voice room"
	}
	if title := strings.TrimSpace(snapshot.GetRoomExt()["title"]); title != "" {
		return title
	}
	if shortID := strings.TrimSpace(snapshot.GetRoomShortId()); shortID != "" {
		return roomShareAppName + " room " + shortID
	}
	return roomShareAppName + " voice room"
}

func roomShareDescription(snapshot *roomv1.RoomSnapshot, owner roomShareOwnerData) string {
	if snapshot != nil {
		if description := strings.TrimSpace(snapshot.GetRoomExt()["description"]); description != "" {
			return description
		}
	}
	if ownerName := roomShareOwnerName(owner); ownerName != "" {
		return "Join " + ownerName + " in a " + roomShareAppName + " voice room."
	}
	return "Join this " + roomShareAppName + " voice room."
}

func roomShareCoverURL(snapshot *roomv1.RoomSnapshot, owner roomShareOwnerData) string {
	if snapshot != nil {
		if coverURL := strings.TrimSpace(snapshot.GetRoomExt()["cover_url"]); coverURL != "" {
			return coverURL
		}
	}
	return strings.TrimSpace(owner.Avatar)
}

func roomShareText(title string, owner roomShareOwnerData) string {
	if ownerName := roomShareOwnerName(owner); ownerName != "" {
		return fmt.Sprintf("Join %s's %s room: %s", ownerName, roomShareAppName, strings.TrimSpace(title))
	}
	return "Join this " + roomShareAppName + " room: " + strings.TrimSpace(title)
}

func roomShareOwnerName(owner roomShareOwnerData) string {
	if owner.Username != "" {
		return owner.Username
	}
	if owner.DisplayUserID != "" {
		return owner.DisplayUserID
	}
	return owner.UserID
}

func roomShareLandingURL(request *http.Request, roomID string, inviteCode string) string {
	landing := url.URL{
		Scheme: roomShareRequestScheme(request),
		Host:   roomShareRequestHost(request),
		Path:   roomShareLandingPathPrefix + url.PathEscape(strings.TrimSpace(roomID)),
	}
	query := landing.Query()
	query.Set("source", "room_share")
	if normalizedInviteCode := strings.TrimSpace(inviteCode); normalizedInviteCode != "" {
		query.Set("invite_code", normalizedInviteCode)
	}
	landing.RawQuery = query.Encode()
	return landing.String()
}

func roomShareDeepLink(roomID string, inviteCode string) string {
	deepLink := url.URL{
		Scheme: roomShareAppScheme,
		Host:   "room",
		Path:   "/" + strings.TrimSpace(roomID),
	}
	query := deepLink.Query()
	query.Set("source", "room_share")
	if normalizedInviteCode := strings.TrimSpace(inviteCode); normalizedInviteCode != "" {
		query.Set("invite_code", normalizedInviteCode)
	}
	deepLink.RawQuery = query.Encode()
	return deepLink.String()
}

func roomShareRequestScheme(request *http.Request) string {
	if forwarded := firstHeaderValue(request.Header.Get("X-Forwarded-Proto")); forwarded == "https" || forwarded == "http" {
		return forwarded
	}
	if request.TLS != nil {
		return "https"
	}
	return "http"
}

func roomShareRequestHost(request *http.Request) string {
	if forwarded := firstHeaderValue(request.Header.Get("X-Forwarded-Host")); forwarded != "" && !strings.ContainsAny(forwarded, "/\\\r\n") {
		return forwarded
	}
	if host := strings.TrimSpace(request.Host); host != "" && !strings.ContainsAny(host, "/\\\r\n") {
		return host
	}
	return "localhost"
}

func firstHeaderValue(raw string) string {
	value := strings.TrimSpace(raw)
	if comma := strings.Index(value, ","); comma >= 0 {
		value = strings.TrimSpace(value[:comma])
	}
	return strings.ToLower(value)
}

func roomShareLandingHTML(data roomShareData) string {
	title := html.EscapeString(data.Title)
	description := html.EscapeString(data.Description)
	imageURL := html.EscapeString(data.CoverURL)
	landingURL := html.EscapeString(data.LandingURL)
	deepLink := html.EscapeString(data.DeepLink)
	appName := html.EscapeString(roomShareAppName)

	return "<!doctype html><html><head>" +
		`<meta charset="utf-8">` +
		`<meta name="viewport" content="width=device-width,initial-scale=1">` +
		"<title>" + title + "</title>" +
		`<meta property="og:type" content="website">` +
		`<meta property="og:site_name" content="` + appName + `">` +
		`<meta property="og:title" content="` + title + `">` +
		`<meta property="og:description" content="` + description + `">` +
		`<meta property="og:url" content="` + landingURL + `">` +
		`<meta property="og:image" content="` + imageURL + `">` +
		`<meta name="twitter:card" content="summary_large_image">` +
		`<meta name="twitter:title" content="` + title + `">` +
		`<meta name="twitter:description" content="` + description + `">` +
		`<meta name="twitter:image" content="` + imageURL + `">` +
		`<meta property="al:android:package" content="` + roomShareAndroidPackage + `">` +
		`<meta property="al:android:app_name" content="` + appName + `">` +
		`<meta property="al:android:url" content="` + deepLink + `">` +
		`<meta property="al:ios:app_name" content="` + appName + `">` +
		`<meta property="al:ios:url" content="` + deepLink + `">` +
		`</head><body>` +
		`<main><h1>` + title + `</h1><p>` + description + `</p><p><a href="` + deepLink + `">Open room</a></p></main>` +
		`</body></html>`
}
