import { Jellyfin } from '@jellyfin/sdk'
import { getUserApi } from '@jellyfin/sdk/lib/utils/api/user-api'
import { getSystemApi } from '@jellyfin/sdk/lib/utils/api/system-api'
import { getLibraryApi } from '@jellyfin/sdk/lib/utils/api/library-api'
import { getItemsApi } from '@jellyfin/sdk/lib/utils/api/items-api'
import { getSessionApi } from '@jellyfin/sdk/lib/utils/api/session-api'
import { getActivityLogApi } from '@jellyfin/sdk/lib/utils/api/activity-log-api'
import { getTvShowsApi } from '@jellyfin/sdk/lib/utils/api/tv-shows-api'
import { getUserViewsApi } from '@jellyfin/sdk/lib/utils/api/user-views-api'
import { getPlaystateApi } from '@jellyfin/sdk/lib/utils/api/playstate-api'
import { getUserLibraryApi } from '@jellyfin/sdk/lib/utils/api/user-library-api'
import { getVideosApi } from '@jellyfin/sdk/lib/utils/api/videos-api'
import { getAudioApi } from '@jellyfin/sdk/lib/utils/api/audio-api'
import { getSearchApi } from '@jellyfin/sdk/lib/utils/api/search-api'
import { getMediaInfoApi } from '@jellyfin/sdk/lib/utils/api/media-info-api'
import { getMediaSegmentsApi } from '@jellyfin/sdk/lib/utils/api/media-segments-api'
import { getPlaylistsApi } from '@jellyfin/sdk/lib/utils/api/playlists-api'
import { getItemRefreshApi } from '@jellyfin/sdk/lib/utils/api/item-refresh-api'
import type { Api } from '@jellyfin/sdk/lib/api'
import { readSecret, removeSecret, writeSecret } from '../lib/sensitive-storage'
import type { AuthState } from './types'

export {
  getUserApi,
  getSystemApi,
  getLibraryApi,
  getItemsApi,
  getSessionApi,
  getActivityLogApi,
  getTvShowsApi,
  getUserViewsApi,
  getPlaystateApi,
  getUserLibraryApi,
  getVideosApi,
  getAudioApi,
  getSearchApi,
  getMediaInfoApi,
  getMediaSegmentsApi,
  getPlaylistsApi,
  getItemRefreshApi,
}

const STORAGE_KEY = 'jf_auth'
const LAST_SERVER_KEY = 'jf_last_server'
const SERVERS_KEY = 'jf_servers'

export function getLastServerUrl(): string {
  try {
    return localStorage.getItem(LAST_SERVER_KEY) || ''
  } catch {
    return ''
  }
}

export function getStoredAuth(): AuthState | null {
  try {
    const raw = readSecret(STORAGE_KEY)
    return raw ? JSON.parse(raw) : null
  } catch {
    return null
  }
}

export function getStoredServerUrl(): string {
  return getStoredAuth()?.serverUrl || ''
}

/**
 * Known-servers list, kept alongside the active `jf_auth` blob. Each
 * entry is a full AuthState (server URL + token + user) so switching
 * between them is one assignment, no re-login required.
 *
 * `getStoredAuth()` is the source of truth for "currently active"; the
 * list mirrors it plus any other servers the user has signed into.
 */
export function getKnownServers(): AuthState[] {
  try {
    const raw = readSecret(SERVERS_KEY)
    if (!raw) {
      const current = getStoredAuth()
      return current ? [current] : []
    }
    const parsed = JSON.parse(raw)
    return Array.isArray(parsed) ? parsed : []
  } catch {
    return []
  }
}

function writeKnownServers(list: AuthState[]) {
  if (list.length === 0) {
    removeSecret(SERVERS_KEY)
  } else {
    writeSecret(SERVERS_KEY, JSON.stringify(list))
  }
}

function serverKey(s: { serverUrl: string; userId: string }): string {
  return `${s.serverUrl}|${s.userId}`
}

export function addKnownServer(state: AuthState) {
  const list = getKnownServers()
  const key = serverKey(state)
  const without = list.filter(s => serverKey(s) !== key)
  writeKnownServers([state, ...without])
}

export function removeKnownServer(state: { serverUrl: string; userId: string }) {
  const list = getKnownServers().filter(s => serverKey(s) !== serverKey(state))
  writeKnownServers(list)
}

function storeAuth(state: AuthState | null) {
  if (state) {
    writeSecret(STORAGE_KEY, JSON.stringify(state))
  } else {
    removeSecret(STORAGE_KEY)
  }
}

let jellyfin: Jellyfin | null = null
let api: Api | null = null

export function getImageUrl(
  serverUrl: string,
  itemId: string,
  imageType: string = 'Primary',
  maxWidth?: number,
  tag?: string | null,
): string {
  if (!serverUrl || !itemId || !tag) return ''
  let url = `${serverUrl}/Items/${itemId}/Images/${imageType}?tag=${tag}`
  if (maxWidth) url += `&maxWidth=${maxWidth}`
  return url
}

export function getBestImage(
  serverUrl: string,
  item: {
    Id?: string | null
    ImageTags?: Record<string, string> | null
    BackdropImageTags?: string[] | null
    ParentBackdropImageTags?: string[] | null
    ParentBackdropItemId?: string | null
    ParentThumbImageTag?: string | null
    ParentThumbItemId?: string | null
    SeriesId?: string | null
    SeriesPrimaryImageTag?: string | null
  },
  preference: 'backdrop' | 'primary' | 'thumb' = 'primary',
  maxWidth?: number,
): string {
  if (!serverUrl || !item.Id) return ''
  const tags = item.ImageTags || {}

  if (preference === 'backdrop') {
    const backdrop = item.BackdropImageTags?.[0]
    if (backdrop) return getImageUrl(serverUrl, item.Id, 'Backdrop', maxWidth, backdrop)
    if (tags.Thumb) return getImageUrl(serverUrl, item.Id, 'Thumb', maxWidth, tags.Thumb)
    const parentBackdrop = item.ParentBackdropImageTags?.[0]
    if (parentBackdrop && item.ParentBackdropItemId) {
      return getImageUrl(serverUrl, item.ParentBackdropItemId, 'Backdrop', maxWidth, parentBackdrop)
    }
    if (tags.Primary) return getImageUrl(serverUrl, item.Id, 'Primary', maxWidth, tags.Primary)
    return ''
  }

  if (preference === 'thumb') {
    if (tags.Thumb) return getImageUrl(serverUrl, item.Id, 'Thumb', maxWidth, tags.Thumb)
    if (tags.Primary) return getImageUrl(serverUrl, item.Id, 'Primary', maxWidth, tags.Primary)
    const parentBackdrop = item.ParentBackdropImageTags?.[0]
    if (parentBackdrop && item.ParentBackdropItemId) {
      return getImageUrl(serverUrl, item.ParentBackdropItemId, 'Backdrop', maxWidth, parentBackdrop)
    }
    if (item.ParentThumbImageTag && item.ParentThumbItemId) {
      return getImageUrl(serverUrl, item.ParentThumbItemId, 'Thumb', maxWidth, item.ParentThumbImageTag)
    }
    if (item.SeriesPrimaryImageTag && item.SeriesId) {
      return getImageUrl(serverUrl, item.SeriesId, 'Primary', maxWidth, item.SeriesPrimaryImageTag)
    }
    return ''
  }

  // primary preference
  if (tags.Primary) return getImageUrl(serverUrl, item.Id, 'Primary', maxWidth, tags.Primary)
  if (item.SeriesPrimaryImageTag && item.SeriesId) {
    return getImageUrl(serverUrl, item.SeriesId, 'Primary', maxWidth, item.SeriesPrimaryImageTag)
  }
  if (tags.Thumb) return getImageUrl(serverUrl, item.Id, 'Thumb', maxWidth, tags.Thumb)
  return ''
}

/**
 * Build a Jellyfin subtitle stream URL for a given track. Uses VTT format
 * which vidstack consumes natively. Returns empty when any param is missing.
 */
export function getSubtitleUrl(
  serverUrl: string,
  itemId: string,
  mediaSourceId: string | null | undefined,
  subtitleStreamIndex: number,
  token: string,
  format: 'vtt' | 'ass' | 'ssa' | 'srt' = 'vtt',
): string {
  if (!serverUrl || !itemId || !token) return ''
  const ms = mediaSourceId || itemId
  return `${serverUrl}/Videos/${itemId}/${ms}/Subtitles/${subtitleStreamIndex}/0/Stream.${format}?api_key=${token}`
}

export function getAudioStreamUrl(
  serverUrl: string,
  itemId: string,
  token: string,
  mediaSourceId?: string,
): string {
  let url = `${serverUrl}/Audio/${itemId}/stream?static=true&api_key=${token}`
  if (mediaSourceId) url += `&MediaSourceId=${mediaSourceId}`
  return url
}

export const jellyfinClient = {
  connect(serverUrl: string): Jellyfin {
    const normalized = serverUrl.replace(/\/+$/, '')
    jellyfin = new Jellyfin({
      clientInfo: { name: 'Jellybloom', version: '0.1.0' },
      deviceInfo: { name: 'Desktop', id: 'jf-desktop-' + Date.now() },
    })
    api = jellyfin.createApi(normalized)
    return jellyfin
  },

  async login(username: string, password: string): Promise<AuthState> {
    if (!api) throw new Error('Not connected to a server')

    const userApi = getUserApi(api)
    const auth = await userApi.authenticateUserByName({
      authenticateUserByName: {
        Username: username.trim(),
        Pw: password,
      },
    })

    const state: AuthState = {
      serverUrl: api.basePath,
      token: auth.data.AccessToken || '',
      userId: auth.data.User?.Id || '',
      userName: auth.data.User?.Name ?? undefined,
    }

    storeAuth(state)
    addKnownServer(state)
    try {
      localStorage.setItem(LAST_SERVER_KEY, state.serverUrl)
    } catch { /* noop */ }
    return state
  },

  /** Set a previously-known server as the active one. */
  activateServer(target: AuthState) {
    storeAuth(target)
    api = jellyfin?.createApi(target.serverUrl, target.token) ?? null
    if (!api) {
      jellyfin = new Jellyfin({
        clientInfo: { name: 'Jellybloom', version: '0.1.0' },
        deviceInfo: { name: 'Desktop', id: 'jf-desktop-' + Date.now() },
      })
      api = jellyfin.createApi(target.serverUrl, target.token)
    }
    try {
      localStorage.setItem(LAST_SERVER_KEY, target.serverUrl)
    } catch { /* noop */ }
  },

  async tryAutoLogin(): Promise<AuthState | null> {
    const stored = getStoredAuth()
    if (!stored) return null

    jellyfin = new Jellyfin({
      clientInfo: { name: 'Jellybloom', version: '0.1.0' },
      deviceInfo: { name: 'Desktop', id: 'jf-desktop-' + Date.now() },
    })
    api = jellyfin.createApi(stored.serverUrl, stored.token)

    try {
      await getSystemApi(api).getSystemInfo()
      return stored
    } catch {
      storeAuth(null)
      api = null
      return null
    }
  },

  logout() {
    storeAuth(null)
    api = null
    jellyfin = null
  },

  isAuthenticated(): boolean {
    return api !== null && !!getStoredAuth()
  },

  getApi(): Api | null {
    return api
  },

  getAuthState(): AuthState | null {
    return getStoredAuth()
  },
}
