import type {
  HistoryListItem,
  HistoryListPage,
  HistoryRecord,
  HistoryUploadPayload,
  HistoryUploadResponse,
  LiveRoomDetail,
  LiveRoomPayload,
  LiveRoomSession,
  LiveRoomSummary,
  LiveRoomUpdatePayload,
  MatchResultsRecord,
} from '../types'

const CERT_NOT_AFTER_STORAGE_KEY = 'badminton-scoreboard::cert-not-after'

// 連線正常時（/api/version 輪詢）把後端回報的憑證到期日存起來，
// 之後 fetch 直接失敗（瀏覽器只給 Failed to fetch，看不出原因）時，
// 就能比對到期日，把「憑證過期」明確顯示給使用者。
export function rememberCertNotAfter(value: string | null | undefined) {
  if (!value || Number.isNaN(Date.parse(value))) {
    return
  }

  try {
    window.localStorage.setItem(CERT_NOT_AFTER_STORAGE_KEY, value)
  } catch {
    // localStorage 不可用時直接略過，只是少了憑證判斷。
  }
}

export function getRememberedCertNotAfter() {
  try {
    const raw = window.localStorage.getItem(CERT_NOT_AFTER_STORAGE_KEY)

    if (!raw) {
      return null
    }

    const time = Date.parse(raw)
    return Number.isNaN(time) ? null : new Date(time)
  } catch {
    return null
  }
}

function buildConnectionErrorMessage() {
  if (typeof navigator !== 'undefined' && navigator.onLine === false) {
    return '目前沒有網路連線，請確認網路後再試。'
  }

  const certNotAfter = getRememberedCertNotAfter()

  if (certNotAfter && certNotAfter.getTime() < Date.now()) {
    const expiredLabel = certNotAfter.toLocaleString('zh-TW', { hour12: false })
    return `無法連線到伺服器：SSL 憑證已於 ${expiredLabel} 過期，請更新伺服器憑證後再試。`
  }

  return '無法連線到伺服器，可能是網路不穩或伺服器離線，請稍後再試。'
}

async function apiFetch(input: string, init?: RequestInit) {
  try {
    return await fetch(input, init)
  } catch {
    throw new Error(buildConnectionErrorMessage())
  }
}

export async function loadMatchResults(time: string) {
  const response = await apiFetch(`/api/match-results/${time}`)
  const payload = (await readJsonSafely(response)) as {
    ok?: boolean
    message?: string
    data?: MatchResultsRecord
  }

  if (response.status === 404) {
    return null
  }

  if (!response.ok || !payload.ok) {
    throw new Error(payload.message ?? '讀取指定日期分組失敗。')
  }

  return payload.data ?? null
}

export async function saveMatchHistory(payload: HistoryUploadPayload) {
  const response = await apiFetch('/api/history', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(payload),
  })

  const result = (await readJsonSafely(response)) as {
    ok?: boolean
    message?: string
    data?: HistoryUploadResponse
  }

  if (!response.ok || !result.ok || !result.data) {
    throw new Error(result.message ?? '上傳戰績失敗。')
  }

  return result.data
}

export async function loadHistoryList(page = 1, pageSize = 20): Promise<HistoryListPage> {
  const response = await apiFetch(`/api/history?page=${page}&pageSize=${pageSize}`)
  const payload = (await readJsonSafely(response)) as {
    ok?: boolean
    message?: string
    data?: HistoryRecord[]
    page?: number
    pageSize?: number
    total?: number
    totalPages?: number
  }

  if (!response.ok || !payload.ok) {
    throw new Error(payload.message ?? '讀取歷史戰績失敗。')
  }

  const items = (payload.data ?? []).map(normalizeHistoryRecord)

  return {
    items,
    page: payload.page ?? page,
    total: payload.total ?? items.length,
    totalPages: payload.totalPages ?? 1,
  }
}

export async function deleteHistoryItem(id: number) {
  const response = await apiFetch(`/api/history/${id}`, {
    method: 'DELETE',
  })

  const payload = (await readJsonSafely(response)) as {
    ok?: boolean
    message?: string
  }

  if (!response.ok || !payload.ok) {
    throw new Error(payload.message ?? '刪除歷史戰績失敗。')
  }
}

export async function createLiveRoom(payload: LiveRoomPayload) {
  const response = await apiFetch('/api/rooms', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(payload),
  })

  const result = (await readJsonSafely(response)) as {
    ok?: boolean
    message?: string
    data?: LiveRoomSession
  }

  if (!response.ok || !result.ok || !result.data) {
    throw new Error(result.message ?? '建立房間失敗。')
  }

  return result.data
}

export async function updateLiveRoom(roomId: string, payload: LiveRoomUpdatePayload) {
  const response = await apiFetch(`/api/rooms/${roomId}`, {
    method: 'PUT',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(payload),
  })

  const result = (await readJsonSafely(response)) as {
    ok?: boolean
    message?: string
    data?: LiveRoomDetail
  }

  if (!response.ok || !result.ok || !result.data) {
    throw new Error(result.message ?? '同步房間比分失敗。')
  }

  return result.data
}

export async function releaseLiveRoom(roomId: string, hostToken: string) {
  const response = await apiFetch(`/api/rooms/${roomId}/release`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ hostToken }),
    keepalive: true,
  })

  const result = (await readJsonSafely(response)) as {
    ok?: boolean
    message?: string
  }

  if (!response.ok || !result.ok) {
    throw new Error(result.message ?? '清除舊房間失敗。')
  }
}

export async function sendLiveRoomHeartbeat(roomId: string, hostToken: string) {
  const response = await apiFetch(`/api/rooms/${roomId}/heartbeat`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ hostToken }),
    keepalive: true,
  })

  const result = (await readJsonSafely(response)) as {
    ok?: boolean
    message?: string
  }

  if (!response.ok || !result.ok) {
    throw new Error(result.message ?? '更新房間心跳失敗。')
  }
}

export async function reconcileLiveRooms() {
  const response = await apiFetch('/api/rooms/reconcile', {
    method: 'POST',
  })

  const result = (await readJsonSafely(response)) as {
    ok?: boolean
    message?: string
    data?: {
      removedRoomIds?: string[]
    }
  }

  if (!response.ok || !result.ok) {
    throw new Error(result.message ?? '清理無主房間失敗。')
  }

  return result.data?.removedRoomIds ?? []
}

export async function loadLiveRoomList() {
  const response = await apiFetch('/api/rooms')
  const result = (await readJsonSafely(response)) as {
    ok?: boolean
    message?: string
    data?: LiveRoomSummary[]
  }

  if (response.status === 404) {
    throw new Error('後端還沒更新到房間功能，請重新部署最新版。')
  }

  if (!response.ok || !result.ok) {
    throw new Error(result.message ?? '載入房間列表失敗。')
  }

  return result.data ?? []
}

export async function loadLiveRoom(roomId: string) {
  const response = await apiFetch(`/api/rooms/${roomId}`)
  const result = (await readJsonSafely(response)) as {
    ok?: boolean
    message?: string
    data?: LiveRoomDetail
  }

  if (response.status === 404) {
    throw new Error('後端還沒更新到房間功能，或這個房間已不存在。')
  }

  if (!response.ok || !result.ok || !result.data) {
    throw new Error(result.message ?? '載入觀戰房間失敗。')
  }

  return result.data
}

export function subscribeRoomList(onMessage: (rooms: LiveRoomSummary[]) => void) {
  const source = new EventSource('/api/rooms/stream')

  source.addEventListener('rooms', (event) => {
    const payload = JSON.parse((event as MessageEvent).data) as LiveRoomSummary[]
    onMessage(payload)
  })

  return () => {
    source.close()
  }
}

export function subscribeLiveRoom(
  roomId: string,
  onMessage: (room: LiveRoomDetail) => void,
  onError?: () => void,
  onRoomClosed?: (payload: { roomId: string; status: string }) => void,
) {
  const source = new EventSource(`/api/rooms/${roomId}/stream`)

  source.addEventListener('room', (event) => {
    const payload = JSON.parse((event as MessageEvent).data) as LiveRoomDetail
    onMessage(payload)
  })

  source.addEventListener('room-closed', (event) => {
    const payload = JSON.parse((event as MessageEvent).data) as {
      roomId: string
      status: string
    }
    onRoomClosed?.(payload)
  })

  source.onerror = () => {
    onError?.()
  }

  return () => {
    source.close()
  }
}

function normalizeHistoryRecord(record: HistoryRecord): HistoryListItem {
  const score = parseJson<[number, number]>(record.score, [0, 0])
  const players = parseJson<string[]>(record.players, [])
  const team = parseJson<[string[], string[]]>(record.team, [[], []])
  const scoreList = parseJson<Array<[number, number, number, 0 | 1]>>(
    record.scoreList,
    [],
  )
  const leftTeamName = team[0]?.join(' / ') || players.slice(0, 2).join(' / ') || '-'
  const rightTeamName = team[1]?.join(' / ') || players.slice(2, 4).join(' / ') || '-'
  const winnerTeamName = score[0] >= score[1] ? leftTeamName : rightTeamName

  return {
    id: record.id,
    time: record.time,
    playedAt: new Date(record.time * 1000).toLocaleString('zh-TW', { hour12: false }),
    dayOfWeek: record.dayOfWeek,
    dayLabel: getDayLabel(record.dayOfWeek),
    score,
    winScore: record.winScore,
    type: record.type,
    typeLabel: record.type === 1 ? '單打' : '雙打',
    players,
    team,
    scoreList,
    leftTeamName,
    rightTeamName,
    winnerTeamName,
  }
}

function parseJson<T>(value: string | null, fallback: T): T {
  if (!value) {
    return fallback
  }

  try {
    return JSON.parse(value) as T
  } catch {
    return fallback
  }
}

function getDayLabel(dayOfWeek: number) {
  const labels = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
  return labels[dayOfWeek] ?? '-'
}

async function readJsonSafely(response: Response) {
  const raw = await response.text()

  if (!raw.trim()) {
    return {}
  }

  try {
    return JSON.parse(raw) as unknown
  } catch {
    throw new Error('伺服器回傳內容不是有效的 JSON。')
  }
}
