package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"gopkg.in/yaml.v3"
	"hyapp/pkg/tencentrtc"
)

type fileConfig struct {
	TencentRTC rtcYAMLConfig `yaml:"tencent_rtc"`
}

type rtcYAMLConfig struct {
	Enabled    bool          `yaml:"enabled"`
	SDKAppID   int64         `yaml:"sdk_app_id"`
	SecretKey  string        `yaml:"secret_key"`
	UserSigTTL time.Duration `yaml:"user_sig_ttl"`
	RoomIDType string        `yaml:"room_id_type"`
	AppScene   string        `yaml:"app_scene"`
}

type server struct {
	rtcConfig tencentrtc.TokenConfig
}

func main() {
	configPath := flag.String("config", "services/gateway-service/configs/config.yaml", "gateway config containing tencent_rtc")
	addr := flag.String("addr", "127.0.0.1:13100", "listen address")
	flag.Parse()

	rtcConfig, err := loadRTCConfig(*configPath)
	if err != nil {
		log.Fatalf("load rtc config failed: %v", err)
	}
	if err := tencentrtc.ValidateConfig(rtcConfig); err != nil {
		log.Fatalf("rtc config is not usable: %v", err)
	}

	s := &server{rtcConfig: rtcConfig}
	mux := http.NewServeMux()
	mux.HandleFunc("/", s.index)
	mux.HandleFunc("/api/token", s.token)
	mux.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) {
		_, _ = w.Write([]byte("ok"))
	})

	log.Printf("TRTC smoke demo listening on http://%s", *addr)
	log.Printf("SDKAppID=%d room_id_type=%s app_scene=%s", rtcConfig.SDKAppID, rtcConfig.RoomIDType, rtcConfig.AppScene)
	if err := http.ListenAndServe(*addr, mux); err != nil {
		log.Fatal(err)
	}
}

func loadRTCConfig(path string) (tencentrtc.TokenConfig, error) {
	content, err := os.ReadFile(path)
	if err != nil {
		return tencentrtc.TokenConfig{}, err
	}
	var cfg fileConfig
	if err := yaml.Unmarshal(content, &cfg); err != nil {
		return tencentrtc.TokenConfig{}, err
	}
	if cfg.TencentRTC.UserSigTTL <= 0 {
		return tencentrtc.TokenConfig{}, errors.New("tencent_rtc.user_sig_ttl is required")
	}
	return tencentrtc.TokenConfig{
		Enabled:    cfg.TencentRTC.Enabled,
		SDKAppID:   cfg.TencentRTC.SDKAppID,
		SecretKey:  strings.TrimSpace(cfg.TencentRTC.SecretKey),
		TTL:        cfg.TencentRTC.UserSigTTL,
		RoomIDType: strings.TrimSpace(cfg.TencentRTC.RoomIDType),
		AppScene:   strings.TrimSpace(cfg.TencentRTC.AppScene),
	}, nil
}

func (s *server) index(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path != "/" {
		http.NotFound(w, r)
		return
	}
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	http.ServeContent(w, r, "index.html", time.Now(), strings.NewReader(indexHTML))
}

func (s *server) token(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	if r.Method != http.MethodGet {
		http.Error(w, `{"error":"method not allowed"}`, http.StatusMethodNotAllowed)
		return
	}

	userID, err := parseUserID(r.URL.Query().Get("user_id"))
	if err != nil {
		writeJSONError(w, http.StatusBadRequest, err)
		return
	}
	roomID := strings.TrimSpace(r.URL.Query().Get("room_id"))
	if roomID == "" {
		writeJSONError(w, http.StatusBadRequest, errors.New("room_id is required"))
		return
	}

	token, err := tencentrtc.GenerateToken(s.rtcConfig, userID, roomID, time.Now().UTC())
	if err != nil {
		writeJSONError(w, http.StatusBadRequest, err)
		return
	}
	_ = json.NewEncoder(w).Encode(token)
}

func parseUserID(raw string) (int64, error) {
	raw = strings.TrimSpace(raw)
	if raw == "" {
		return 0, errors.New("user_id is required")
	}
	value, err := strconv.ParseInt(raw, 10, 64)
	if err != nil || value <= 0 {
		return 0, fmt.Errorf("user_id must be a positive int64")
	}
	return value, nil
}

func writeJSONError(w http.ResponseWriter, status int, err error) {
	w.WriteHeader(status)
	_ = json.NewEncoder(w).Encode(map[string]string{"error": err.Error()})
}

const indexHTML = `<!doctype html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>TRTC Smoke</title>
  <script src="https://web.sdk.qcloud.com/trtc/webrtc/v5/dist/trtc.js"></script>
  <style>
    :root { color-scheme: light; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
    body { margin: 0; background: #f6f8fb; color: #151922; }
    main { max-width: 920px; margin: 0 auto; padding: 28px; }
    h1 { margin: 0 0 20px; font-size: 24px; }
    section { background: #fff; border: 1px solid #e4e8f0; border-radius: 8px; padding: 18px; margin-bottom: 16px; }
    label { display: block; font-size: 13px; color: #526070; margin-bottom: 6px; }
    input, select { width: 100%; box-sizing: border-box; padding: 10px 12px; border: 1px solid #cfd6e4; border-radius: 6px; font-size: 14px; }
    .grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
    .actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 14px; }
    button { border: 1px solid #2f6df6; background: #2f6df6; color: #fff; padding: 10px 14px; border-radius: 6px; font-size: 14px; cursor: pointer; }
    button.secondary { background: #fff; color: #2f6df6; }
    button:disabled { opacity: .55; cursor: not-allowed; }
    pre { min-height: 260px; max-height: 420px; overflow: auto; margin: 0; padding: 14px; background: #0d1117; color: #d8dee9; border-radius: 8px; font-size: 12px; line-height: 1.5; white-space: pre-wrap; word-break: break-word; }
    .status { font-size: 14px; margin: 0 0 10px; }
    .ok { color: #0b7a36; }
    .bad { color: #b42318; }
    @media (max-width: 720px) { main { padding: 18px; } .grid { grid-template-columns: 1fr; } }
  </style>
</head>
<body>
  <main>
    <h1>TRTC Smoke</h1>
    <section>
      <div class="grid">
        <div>
          <label for="userId">user_id / rtc_user_id</label>
          <input id="userId" value="123456" autocomplete="off" />
        </div>
        <div>
          <label for="roomId">str_room_id</label>
          <input id="roomId" autocomplete="off" />
        </div>
        <div>
          <label for="scene">scene</label>
          <select id="scene">
            <option value="live" selected>live + audience</option>
            <option value="rtc">rtc</option>
          </select>
        </div>
        <div>
          <label for="role">role</label>
          <select id="role">
            <option value="audience" selected>audience</option>
            <option value="anchor">anchor</option>
          </select>
        </div>
      </div>
      <div class="actions">
        <button id="enterBtn">Enter Room</button>
        <button id="exitBtn" class="secondary" disabled>Exit Room</button>
        <button id="audioBtn" class="secondary" disabled>Start Local Audio</button>
        <button id="clearBtn" class="secondary">Clear Log</button>
      </div>
    </section>
    <section>
      <p id="status" class="status">Ready</p>
      <pre id="log"></pre>
    </section>
  </main>

  <script>
    const $ = id => document.getElementById(id);
    const state = { trtc: null, inRoom: false, audio: false };

    function defaultRoomId() {
      const suffix = Math.random().toString(16).slice(2, 10);
      return "trtc_smoke_" + suffix;
    }

    function appendLog(message, data) {
      const ts = new Date().toISOString();
      const line = data === undefined ? message : message + " " + safeStringify(data);
      $("log").textContent += "[" + ts + "] " + line + "\n";
      $("log").scrollTop = $("log").scrollHeight;
    }

    function safeStringify(value) {
      try {
        if (value instanceof Error) {
          const errorData = { name: value.name, message: value.message, stack: value.stack };
          for (const key of Object.keys(value)) errorData[key] = value[key];
          return JSON.stringify(errorData);
        }
        return JSON.stringify(value);
      } catch (_) {
        return String(value);
      }
    }

    function setStatus(text, ok) {
      $("status").textContent = text;
      $("status").className = "status " + (ok ? "ok" : "bad");
    }

    function updateButtons() {
      $("enterBtn").disabled = state.inRoom;
      $("exitBtn").disabled = !state.inRoom;
      $("audioBtn").disabled = !state.inRoom || $("role").value !== "anchor";
      $("audioBtn").textContent = state.audio ? "Stop Local Audio" : "Start Local Audio";
    }

    async function fetchToken() {
      const params = new URLSearchParams({
        user_id: $("userId").value.trim(),
        room_id: $("roomId").value.trim()
      });
      const resp = await fetch("/api/token?" + params.toString());
      const data = await resp.json();
      if (!resp.ok) throw new Error(data.error || "token request failed");
      return data;
    }

    function bindEvents(trtc) {
      const events = TRTC.EVENT || {};
      for (const key of ["ERROR", "KICKED_OUT", "REMOTE_USER_ENTER", "REMOTE_USER_EXIT", "REMOTE_AUDIO_AVAILABLE", "REMOTE_AUDIO_UNAVAILABLE", "NETWORK_QUALITY"]) {
        if (events[key]) {
          trtc.on(events[key], event => appendLog("event." + key, event));
        }
      }
    }

    async function enterRoom() {
      try {
        setStatus("Requesting token...", true);
        appendLog("browser", { userAgent: navigator.userAgent });
        if (TRTC.isSupported) {
          appendLog("isSupported", await TRTC.isSupported());
        }
        const token = await fetchToken();
        appendLog("token", {
          sdk_app_id: token.sdk_app_id,
          rtc_user_id: token.rtc_user_id,
          str_room_id: token.str_room_id,
          expire_at_ms: token.expire_at_ms,
          role: token.role,
          app_scene: token.app_scene
        });

        state.trtc = TRTC.create();
        bindEvents(state.trtc);
        const roomOptions = {
          sdkAppId: token.sdk_app_id,
          userId: token.rtc_user_id,
          userSig: token.user_sig,
          strRoomId: token.str_room_id
        };
        if ($("scene").value === "live") {
          roomOptions.scene = TRTC.TYPE.SCENE_LIVE;
          roomOptions.role = $("role").value === "anchor" ? TRTC.TYPE.ROLE_ANCHOR : TRTC.TYPE.ROLE_AUDIENCE;
        } else {
          roomOptions.scene = TRTC.TYPE.SCENE_RTC;
        }

        appendLog("enterRoom.options", {
          sdkAppId: roomOptions.sdkAppId,
          userId: roomOptions.userId,
          strRoomId: roomOptions.strRoomId,
          scene: roomOptions.scene,
          role: roomOptions.role
        });
        setStatus("Entering room...", true);
        await state.trtc.enterRoom(roomOptions);
        state.inRoom = true;
        setStatus("Entered room", true);
        appendLog("enterRoom.success");
      } catch (err) {
        setStatus("Enter failed", false);
        appendLog("enterRoom.error", err);
        if (state.trtc) {
          try { await state.trtc.destroy(); } catch (_) {}
          state.trtc = null;
        }
      } finally {
        updateButtons();
      }
    }

    async function exitRoom() {
      try {
        if (state.audio) await toggleAudio();
        if (state.trtc) {
          await state.trtc.exitRoom();
          await state.trtc.destroy();
        }
        appendLog("exitRoom.success");
        setStatus("Exited room", true);
      } catch (err) {
        setStatus("Exit failed", false);
        appendLog("exitRoom.error", err);
      } finally {
        state.trtc = null;
        state.inRoom = false;
        state.audio = false;
        updateButtons();
      }
    }

    async function toggleAudio() {
      if (!state.trtc) return;
      if (!state.audio) {
        await state.trtc.startLocalAudio();
        state.audio = true;
        appendLog("startLocalAudio.success");
      } else {
        await state.trtc.stopLocalAudio();
        state.audio = false;
        appendLog("stopLocalAudio.success");
      }
      updateButtons();
    }

    $("roomId").value = defaultRoomId();
    $("enterBtn").addEventListener("click", enterRoom);
    $("exitBtn").addEventListener("click", exitRoom);
    $("audioBtn").addEventListener("click", () => toggleAudio().catch(err => appendLog("audio.error", err)));
    $("clearBtn").addEventListener("click", () => { $("log").textContent = ""; });
    $("role").addEventListener("change", updateButtons);
    updateButtons();
  </script>
</body>
</html>`
