<template>
  <view class="page">
    <view class="panel">
      <text class="title">SyncTV</text>
      <text class="status">{{ status }}</text>

      <view class="field">
        <text class="label">Server</text>
        <input v-model="server" class="input" />
      </view>

      <view class="field">
        <text class="label">Room Code</text>
        <input v-model="roomCode" class="input" />
      </view>

      <view class="row">
        <button class="primary" @click="createRoom">Create</button>
        <button @click="joinRoom">Join</button>
      </view>

      <view class="divider" />

      <text class="section">Source</text>
      <picker :range="sourceTypes" :value="sourceTypeIndex" @change="sourceTypeIndex = Number($event.detail.value)">
        <view class="select">Type: {{ sourceTypes[sourceTypeIndex] }}</view>
      </picker>
      <picker :range="sourceModes" :value="sourceModeIndex" @change="sourceModeIndex = Number($event.detail.value)">
        <view class="select">Connection: {{ sourceModes[sourceModeIndex] }}</view>
      </picker>
      <view class="field">
        <text class="label">URL</text>
        <input v-model="sourceUrl" class="input" />
      </view>
      <view class="field">
        <text class="label">Username</text>
        <input v-model="username" class="input" />
      </view>
      <view class="field">
        <text class="label">Password / Token</text>
        <input v-model="password" password class="input" />
      </view>
      <label class="checkbox">
        <checkbox :checked="isLive" @click="isLive = !isLive" />
        <text>Live stream</text>
      </label>
      <button :disabled="!isOwner" class="primary" @click="applySource">Apply Source</button>
    </view>

    <view v-if="player.hasNative" class="native-player">
      <text class="native-player-text">VLC native player active</text>
    </view>
    <video
      v-else
      id="player"
      class="player"
      :src="playerUrl"
      controls
      autoplay="false"
      @timeupdate="onTimeUpdate"
    />

    <view class="controls">
      <button @click="play">Play</button>
      <button @click="pause">Pause</button>
      <button @click="syncToLive">Back To Live</button>
    </view>

    <text class="error">{{ error }}</text>
  </view>
</template>

<script setup>
import { onBeforeUnmount, ref } from 'vue'
import { createApi } from '../../common/api'
import { getDeviceId } from '../../common/device'
import { createPlayer } from '../../common/player'
import { SyncClient } from '../../common/sync'

const server = ref('http://yuyun-us1.stormrain.cn:8088')
const roomCode = ref('')
const status = ref('Not connected')
const error = ref('')
const isOwner = ref(false)
const deviceId = getDeviceId()

const sourceTypes = ['url', 'oss', 'webdav', 'live']
const sourceModes = ['direct', 'proxy']
const sourceTypeIndex = ref(0)
const sourceModeIndex = ref(0)
const sourceUrl = ref('')
const username = ref('')
const password = ref('')
const isLive = ref(false)
const playerUrl = ref('')
const currentTimeMs = ref(0)
const player = createPlayer({
  onError(message) {
    error.value = message
  }
})

const sync = new SyncClient({
  onEvent: handleEvent,
  onError(message) {
    error.value = message
  }
})

function api() {
  return createApi(server.value)
}

async function createRoom() {
  try {
    error.value = ''
    const data = await api().createRoom()
    roomCode.value = data.room.code
    await joinRoom()
  } catch (err) {
    error.value = err.message
  }
}

async function joinRoom() {
  try {
    error.value = ''
    const snapshot = await api().getRoom(roomCode.value.toUpperCase(), deviceId)
    applySnapshot(snapshot)
    sync.connect(server.value, roomCode.value.toUpperCase(), deviceId)
  } catch (err) {
    error.value = err.message
  }
}

function applySource() {
  sync.setSource({
    type: sourceTypes[sourceTypeIndex.value],
    mode: sourceModes[sourceModeIndex.value],
    url: sourceUrl.value,
    isLive: isLive.value || sourceTypes[sourceTypeIndex.value] === 'live',
    username: username.value,
    password: password.value
  })
}

function play() {
  sync.play(currentTimeMs.value)
  if (!player.play()) {
    uni.createVideoContext('player').play()
  }
}

function pause() {
  sync.pause(currentTimeMs.value)
  if (!player.pause()) {
    uni.createVideoContext('player').pause()
  }
}

function syncToLive() {
  sync.syncToLive()
}

function onTimeUpdate(event) {
  currentTimeMs.value = Math.floor(event.detail.currentTime * 1000)
}

function handleEvent(type, payload) {
  if (type === 'roomSnapshot' || type === 'sourceChanged') {
    applySnapshot(payload)
  } else if (type === 'playbackChanged') {
    applyPlayback(payload.playback, payload.serverNow)
  } else if (type === 'presenceChanged') {
    status.value = `${roomCode.value} · ${isOwner.value ? 'Owner' : 'Member'} · ${payload.onlineCount} online`
  } else if (type === 'error') {
    error.value = payload.message || 'unknown error'
  }
}

function applySnapshot(snapshot) {
  roomCode.value = snapshot.room.code
  isOwner.value = snapshot.isOwner
  status.value = `${snapshot.room.code} · ${snapshot.isOwner ? 'Owner' : 'Member'} · ${snapshot.room.onlineCount} online`
  if (snapshot.room.source) {
    const source = snapshot.room.source
    sourceUrl.value = source.url
    isLive.value = !!source.isLive
    sourceTypeIndex.value = Math.max(0, sourceTypes.indexOf(source.type))
    sourceModeIndex.value = Math.max(0, sourceModes.indexOf(source.mode))
    playerUrl.value = player.open(source, server.value)
  }
  if (snapshot.room.playback) {
    applyPlayback(snapshot.room.playback, snapshot.serverNow)
  }
}

function applyPlayback(playback, serverNow) {
  const ctx = uni.createVideoContext('player')
  if (playback.state === 'playing') {
    const target = playback.positionMs + Math.max(0, serverNow - playback.serverUpdatedAt)
    if (target > 0) {
      if (!player.seek(target)) ctx.seek(target / 1000)
    }
    if (!player.play()) ctx.play()
  } else if (playback.state === 'paused') {
    if (playback.positionMs > 0) {
      if (!player.seek(playback.positionMs)) ctx.seek(playback.positionMs / 1000)
    }
    if (!player.pause()) ctx.pause()
  }
}

onBeforeUnmount(() => {
  sync.close()
  player.release()
})
</script>

<style>
.page {
  padding: 24rpx;
}
.panel {
  background: #ffffff;
  border-radius: 12rpx;
  padding: 24rpx;
}
.title {
  display: block;
  font-size: 44rpx;
  font-weight: 700;
}
.status {
  display: block;
  color: #64748b;
  margin: 8rpx 0 24rpx;
}
.field {
  margin-bottom: 18rpx;
}
.label {
  display: block;
  font-weight: 600;
  margin-bottom: 8rpx;
}
.input,
.select {
  border: 1px solid #cbd5e1;
  border-radius: 8rpx;
  padding: 16rpx;
  background: #ffffff;
}
.row {
  display: flex;
  gap: 16rpx;
}
.primary {
  background: #2563eb;
  color: #ffffff;
}
.divider {
  height: 1px;
  background: #e2e8f0;
  margin: 24rpx 0;
}
.section {
  display: block;
  font-size: 32rpx;
  font-weight: 700;
  margin-bottom: 16rpx;
}
.checkbox {
  display: flex;
  align-items: center;
  margin: 14rpx 0;
}
.player {
  width: 100%;
  height: 420rpx;
  margin-top: 24rpx;
  background: #000000;
}
.native-player {
  height: 420rpx;
  margin-top: 24rpx;
  background: #000000;
  display: flex;
  align-items: center;
  justify-content: center;
}
.native-player-text {
  color: #ffffff;
}
.controls {
  display: flex;
  gap: 12rpx;
  margin-top: 18rpx;
}
.error {
  display: block;
  color: #dc2626;
  margin-top: 18rpx;
}
</style>
