package meet

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"time"
)

type Config struct {
	AppID     string
	AppSecret string
	Domain    string
}

type RoomToken struct {
	Token   string `json:"token"`
	Room    string `json:"room"`
	Domain  string `json:"domain"`
	MeetURL string `json:"meet_url"`
}

type UserInfo struct {
	ID     string `json:"id"`
	Name   string `json:"name"`
	Email  string `json:"email"`
	Avatar string `json:"avatar,omitempty"`
	IsMod  bool   `json:"is_moderator"`
}

func NewConfig(appID, appSecret, domain string) *Config {
	return &Config{
		AppID:     appID,
		AppSecret: appSecret,
		Domain:    domain,
	}
}

func (c *Config) GenerateToken(room string, user *UserInfo, ttl time.Duration) (*RoomToken, error) {
	now := time.Now()
	exp := now.Add(ttl)

	header := map[string]string{
		"alg": "HS256",
		"typ": "JWT",
	}

	payload := map[string]any{
		"iss":  c.AppID,
		"sub":  "meet.jitsi",
		"aud":  "ulti",
		"iat":  now.Unix(),
		"exp":  exp.Unix(),
		"room": room,
		"context": map[string]any{
			"user": map[string]any{
				"id":        user.ID,
				"name":      user.Name,
				"email":     user.Email,
				"avatar":    user.Avatar,
				"moderator": user.IsMod,
			},
		},
	}

	token, err := signJWT(header, payload, c.AppSecret)
	if err != nil {
		return nil, err
	}

	return &RoomToken{
		Token:   token,
		Room:    room,
		Domain:  "meet.jitsi",
		MeetURL: fmt.Sprintf("https://%s/meet/%s?jwt=%s", c.Domain, room, token),
	}, nil
}

func signJWT(header map[string]string, payload map[string]any, secret string) (string, error) {
	headerJSON, err := json.Marshal(header)
	if err != nil {
		return "", err
	}
	payloadJSON, err := json.Marshal(payload)
	if err != nil {
		return "", err
	}

	headerB64 := base64URLEncode(headerJSON)
	payloadB64 := base64URLEncode(payloadJSON)
	signingInput := headerB64 + "." + payloadB64

	mac := hmac.New(sha256.New, []byte(secret))
	mac.Write([]byte(signingInput))
	signature := base64URLEncode(mac.Sum(nil))

	return signingInput + "." + signature, nil
}

func base64URLEncode(data []byte) string {
	return base64.RawURLEncoding.EncodeToString(data)
}
