/**
 * API-клиент админ-эндпоинтов (`/api/v1/admin/*`).
 * Списковые DTO (`AdminConferenceOut`/`AdminUserOut`) отдаются в стандартном
 * конверте пагинации `items`/`total`.
 */
import { apiRequest } from '@/api/client'
import type { ConferenceRecurrence, ConferenceStatus, PublishQualityCap, SummaryRecipientsMode } from '@/api/conferences'

/** Уровень качества AI-обработки (транскрибация + суммаризация). */
export type AiLevel = 'min' | 'medium' | 'max'

/** Доступность одного уровня AI с причиной отказа (если недоступен). */
export interface AiLevelStatus {
  level: AiLevel
  available: boolean
  reason: string | null
}

/** Эффективные настройки инстанса. */
export interface SettingsOut {
  chat_enabled: boolean
  /** Включён ли модуль «поднятие руки» — кнопка «Рука» и очередь целиком. */
  hand_queue_enabled: boolean
  /** Единый переключатель модуля AI (транскрибация + суммаризация). */
  transcription_enabled: boolean
  /** Есть ли хотя бы один Celery-воркер, обслуживающий очередь транскрибации. */
  transcription_queue_served: boolean
  ai_level: AiLevel
  ai_levels: AiLevelStatus[]
  summary_recipients: SummaryRecipientsMode
  /** IANA-таймзона отображения времени в письмах/.ics (напр. `Europe/Moscow`). */
  display_timezone: string
  /** Разрешён ли выбор команды на экране регистрации. */
  registration_team_choice: boolean
  /** Включена ли верификация регистрации по домену корпоративной почты. */
  registration_email_domain_enabled: boolean
  /** Эталонные домены для верификации (напр. `['company.ru']`) — email подходит, если
   * совпадает с любым из списка; пуст, если верификация выключена. */
  registration_email_domains: string[]
  /** Включён ли контактный адрес инстанса (подставляется в `Reply-To` исходящих писем). */
  contact_email_enabled: boolean
  /** Контактный адрес — `null`, если не задан/выключен. */
  contact_email: string | null
  /** Потолок качества исходящего видео публикующего — см. `PublishQualityCap`. */
  publish_quality_cap: PublishQualityCap
  /** Максимум одновременно видимых плиток сцены (`StageGrid`). */
  stage_max_tiles: number
  /** Обязательна ли галочка согласия на обработку персональных данных при регистрации. */
  consent_required: boolean
  /** Текст регламента (редактируемый шаблон, дефолт — типовой образец без юридической силы). */
  consent_policy_text: string
  /** Номер редакции текста — растёт при каждом изменении `consent_policy_text`. */
  consent_policy_version: number
  /** Проверка устройств на входе (сессия 33) — запрос доступа к камере/микрофону
   * и превью камеры на странице логина и в карточке «Как вас зовут?». */
  device_check_enabled: boolean
  /** Замена фона видео на картинку (сессия 35) — кнопка «Фон» в комнате,
   * выбор фона в превью на входе и раздел «Свои фоны» в профиле. */
  virtual_background_enabled: boolean
}

/** Тело частичного обновления настроек инстанса — все поля опциональны. */
export interface SettingsUpdateIn {
  chat_enabled?: boolean
  hand_queue_enabled?: boolean
  transcription_enabled?: boolean
  /** Недоступный уровень (см. `ai_levels`) — backend отвечает 400. */
  ai_level?: AiLevel
  summary_recipients?: SummaryRecipientsMode
  display_timezone?: string
  registration_team_choice?: boolean
  /** Включение с пустым списком или невалидным доменом — backend отвечает 400. */
  registration_email_domain_enabled?: boolean
  registration_email_domains?: string[]
  /** Включение без email или невалидный email — backend отвечает 400. */
  contact_email_enabled?: boolean
  contact_email?: string | null
  publish_quality_cap?: PublishQualityCap
  stage_max_tiles?: number
  /** Включение с пустым текстом регламента — backend отвечает 400. */
  consent_required?: boolean
  consent_policy_text?: string
  device_check_enabled?: boolean
  virtual_background_enabled?: boolean
}

/** Тело запроса тестовой отправки письма (`POST /admin/settings/test-email`). */
export interface TestEmailIn {
  /** Не задан — backend отправит на email текущего администратора. */
  to?: string | null
}

/** Результат тестовой отправки — успех или текст ошибки транспорта. */
export interface TestEmailOut {
  success: boolean
  message: string
  /** Хост/порт SMTP — только при `EMAIL_BACKEND=smtp`, без логина/пароля. */
  smtp_host: string | null
  smtp_port: number | null
}

/** Конференция в ответе админ-списка — `ConferenceOut` + сведения о владельце. */
export interface AdminConferenceOut {
  id: string
  number: string
  slug: string
  title: string | null
  status: ConferenceStatus
  is_pinned: boolean
  is_closed: boolean
  scheduled_at: string | null
  duration_minutes: number | null
  recurrence: ConferenceRecurrence | null
  next_occurrence: string | null
  created_at: string
  summary_recipients: SummaryRecipientsMode | null
  owner_name: string | null
  owner_email: string | null
}

/** Параметры выборки списка конференций. */
export interface AdminConferenceQuery {
  status?: ConferenceStatus
  q?: string
  limit?: number
  offset?: number
}

/** Тело правки конференции админом — те же поля, что и в пользовательском API. */
export interface AdminConferenceUpdatePayload {
  title?: string
  scheduled_at?: string
  duration_minutes?: number
  is_pinned?: boolean
  recurrence?: ConferenceRecurrence
  is_closed?: boolean
  password?: string
  summary_recipients?: SummaryRecipientsMode | null
}

/** Пользователь в ответе админ-списка. */
export interface AdminUserOut {
  id: string
  email: string
  name_user: string
  role: 'admin' | 'user'
  is_blocked: boolean
  email_verified: boolean
  created_at: string
  /** Команда пользователя — `null`, если не назначена. */
  team_id: string | null
  /** URL аватара с cache-busting (`?v=`) — `null`, если фото не загружено. */
  avatar_url: string | null
  team_name: string | null
}

/**
 * Карточка профиля пользователя для админки — тот же
 * набор полей, что и в списке (backend отдаёт аватар и команду и в списке,
 * и в карточке).
 */
export type AdminUserDetailOut = AdminUserOut

/** Параметры выборки списка пользователей. */
export interface AdminUserQuery {
  /** Фильтр по блокировке — без параметра отдаются все пользователи. */
  status?: 'active' | 'blocked'
  q?: string
  limit?: number
  offset?: number
}

/** Тело правки пользователя — роль, блокировка, ФИО и/или команда, все поля опциональны. */
export interface AdminUserUpdatePayload {
  role?: 'admin' | 'user'
  is_blocked?: boolean
  name_user?: string
  /** `null` — снять пользователя с команды. */
  team_id?: string | null
}

/** Тело создания пользователя администратором. */
export interface AdminUserCreateIn {
  name_user: string
  email: string
  /** Та же политика минимальной длины, что и при самостоятельной регистрации. */
  password: string
  team_id?: string | null
}

/** Команда в ответе админ-списка. */
export interface TeamOut {
  id: string
  name: string
  created_at: string
}

/** Параметры выборки списка команд. */
export interface AdminTeamQuery {
  limit?: number
  offset?: number
}

/** Стандартный конверт пагинации списковых админ-эндпоинтов. */
export interface PagedResult<T> {
  items: T[]
  total: number
}

function toQueryString(params: Record<string, string | number | undefined>): string {
  const search = new URLSearchParams()
  for (const [key, value] of Object.entries(params)) {
    if (value !== undefined && value !== '') search.set(key, String(value))
  }
  const qs = search.toString()
  return qs ? `?${qs}` : ''
}

/** Список конференций для админки — с фильтром по статусу, поиском и пагинацией. */
export async function listAdminConferences(query: AdminConferenceQuery = {}): Promise<PagedResult<AdminConferenceOut>> {
  const qs = toQueryString({ status: query.status, q: query.q, limit: query.limit, offset: query.offset })
  return apiRequest<PagedResult<AdminConferenceOut>>(`/admin/conferences${qs}`)
}

/** Частично обновить конференцию от имени администратора (реюз `ConferenceService.update`). */
export async function updateAdminConference(
  id: string,
  payload: AdminConferenceUpdatePayload,
): Promise<AdminConferenceOut> {
  return apiRequest<AdminConferenceOut>(`/admin/conferences/${id}`, { method: 'PATCH', body: payload })
}

/** Удалить конференцию навсегда от имени администратора. 409 — конференция сейчас идёт. */
export async function deleteAdminConference(id: string): Promise<void> {
  await apiRequest(`/admin/conferences/${id}`, { method: 'DELETE' })
}

/**
 * Ручная рассылка .ics-приглашений по конференции. Без `emails` — получатели
 * по умолчанию (владелец + участники прошлых сеансов для закреплённых).
 * 202 — задача поставлена в очередь, письма отправляются асинхронно.
 */
export async function sendConferenceInvitations(id: string, emails?: string[]): Promise<void> {
  await apiRequest(`/admin/conferences/${id}/invitations`, {
    method: 'POST',
    body: emails && emails.length > 0 ? { emails } : {},
  })
}

/** Список пользователей для админки — с поиском и пагинацией. */
export async function listAdminUsers(query: AdminUserQuery = {}): Promise<PagedResult<AdminUserOut>> {
  const qs = toQueryString({ status: query.status, q: query.q, limit: query.limit, offset: query.offset })
  return apiRequest<PagedResult<AdminUserOut>>(`/admin/users${qs}`)
}

/** Сменить роль и/или блокировку пользователя. 409 — попытка изменить свою учётную запись. */
export async function updateAdminUser(id: string, payload: AdminUserUpdatePayload): Promise<AdminUserOut> {
  return apiRequest<AdminUserOut>(`/admin/users/${id}`, { method: 'PATCH', body: payload })
}

/**
 * Создать пользователя от имени администратора. 201 — email
 * считается подтверждённым сразу. 409 — email уже зарегистрирован,
 * 404 `team_not_found` — указанная команда не существует.
 */
export async function createAdminUser(payload: AdminUserCreateIn): Promise<AdminUserOut> {
  return apiRequest<AdminUserOut>('/admin/users', { method: 'POST', body: payload })
}

/** Карточка профиля пользователя — открывается по клику на имя в таблице «Пользователи». */
export async function getAdminUser(id: string): Promise<AdminUserDetailOut> {
  return apiRequest<AdminUserDetailOut>(`/admin/users/${id}`)
}

/** Загрузить аватар пользователя от имени администратора. 413/415 — лимиты размера/типа файла. */
export async function uploadAdminUserAvatar(id: string, file: File): Promise<AdminUserDetailOut> {
  const form = new FormData()
  form.append('file', file)
  return apiRequest<AdminUserDetailOut>(`/admin/users/${id}/avatar`, { method: 'POST', body: form })
}

/** Текущие настройки инстанса. */
export async function getAdminSettings(): Promise<SettingsOut> {
  return apiRequest<SettingsOut>('/admin/settings')
}

/** Частично обновить настройки инстанса. 400 — недоступный уровень AI/невалидная таймзона. */
export async function updateAdminSettings(payload: SettingsUpdateIn): Promise<SettingsOut> {
  return apiRequest<SettingsOut>('/admin/settings', { method: 'PUT', body: payload })
}

/**
 * Отправить тестовое письмо синхронно — проверка почтовой конфигурации.
 * Результат приходит в теле ответа (`success`/`message`), не через HTTP-статус ошибки.
 */
export async function sendTestEmail(payload: TestEmailIn = {}): Promise<TestEmailOut> {
  return apiRequest<TestEmailOut>('/admin/settings/test-email', { method: 'POST', body: payload })
}

/** Список команд для админки — с пагинацией. */
export async function listAdminTeams(query: AdminTeamQuery = {}): Promise<PagedResult<TeamOut>> {
  const qs = toQueryString({ limit: query.limit, offset: query.offset })
  return apiRequest<PagedResult<TeamOut>>(`/admin/teams${qs}`)
}

/** Создать команду. 409 — имя уже занято. */
export async function createAdminTeam(name: string): Promise<TeamOut> {
  return apiRequest<TeamOut>('/admin/teams', { method: 'POST', body: { name } })
}

/** Переименовать команду. 409 — имя уже занято. */
export async function renameAdminTeam(id: string, name: string): Promise<TeamOut> {
  return apiRequest<TeamOut>(`/admin/teams/${id}`, { method: 'PATCH', body: { name } })
}

/** Удалить команду навсегда. */
export async function deleteAdminTeam(id: string): Promise<void> {
  await apiRequest(`/admin/teams/${id}`, { method: 'DELETE' })
}
