/**
 * Admin API client (U9 — Admin Console).
 *
 * Talks to the server-side ``/api/v1/admin/...`` endpoints implemented
 * in ``src/agentkit/server/routes/auth.py`` (sessions) and
 * ``src/agentkit/server/routes/admin.py`` (departments, users, LLM
 * config, quotas, skills, KB, usage). All endpoints require the
 * ``USER_MANAGE`` permission (admin role); the API client does not
 * itself enforce that — the server's 403 response is surfaced to the
 * caller as an :class:`IApiError` with ``status: 403``.
 *
 * Surface (grouped by domain):
 * - Sessions: ``listAllSessions`` / ``listUserSessions`` / ``revokeUserSession``
 * - Departments: CRUD + enable/disable + skill/KB bindings
 * - Users: CRUD + reset-password + department assignment
 * - LLM config: provider CRUD + api-key + fallback chains
 * - Quotas: per-department quota CRUD
 * - Skills: enable/disable + import + reload + config update
 * - KB: documents list/upload/delete + source sync/rebuild
 * - Usage: summary / timeseries / by-model / top-users / export
 */

import { BaseApiClient } from './base'
import type { ISessionInfo } from './auth'

// ---------------------------------------------------------------------------
// TypeScript interfaces — admin domain models
// ---------------------------------------------------------------------------

/** Department record (matches backend ``departments`` table projection). */
export interface IDepartment {
  id: string
  name: string
  description: string
  is_active: boolean
  created_at: string
}

/** Admin user record (matches backend ``users`` table projection + departments). */
export interface IAdminUser {
  id: string
  username: string
  email: string
  role: string
  is_active: boolean
  is_terminal_authorized: boolean
  is_server_terminal_authorized?: boolean
  created_at: string
  departments?: IDepartment[]
}

/** LLM provider record (API keys are masked by the server). */
export interface ILlmProvider {
  name: string
  type: string
  api_key: string
  base_url: string
  models: Record<string, unknown>
  max_tokens: number
  timeout: number
}

/** Quota record for a department. */
export interface IQuota {
  id: string
  department_id: string
  quota_type: string
  limit_value: number | string[]
  period: string
  updated_at: string
}

/** KB document record. */
export interface IKbDocument {
  document_id: string
  filename: string
  source_id: string
  chunks: number
  status: string
  created_at: string
  department_id?: string
}

/** Usage summary aggregation. */
export interface IUsageSummary {
  total_tokens: number
  total_cost: number
  total_requests: number
  by_model: Record<string, { total_tokens: number; total_cost: number; count: number }>
  by_user: Record<string, { total_tokens: number; total_cost: number; count: number }>
  by_department: Record<string, { total_tokens: number; total_cost: number; count: number }>
}

/** Usage timeseries bucket. */
export interface IUsageTimeseries {
  timestamp: string
  tokens: number
  cost: number
  requests: number
}

/** Per-model usage breakdown row. */
export interface IUsageByModel {
  model: string
  tokens: number
  cost: number
  requests: number
}

/** Top-user usage row. */
export interface IUsageTopUser {
  user_id: string
  tokens: number
  cost: number
  requests: number
}

class AdminApiClient extends BaseApiClient {
  constructor(baseUrl: string = '/api/v1') {
    super(baseUrl)
  }

  // -----------------------------------------------------------------------
  // Sessions (existing — kept for backwards compat)
  // -----------------------------------------------------------------------

  /**
   * List recent sessions across all users (admin only).
   *
   * @param limit Maximum number of sessions to return (default 200).
   */
  async listAllSessions(limit: number = 200): Promise<ISessionInfo[]> {
    return this.request<ISessionInfo[]>(
      `/admin/sessions?limit=${encodeURIComponent(String(limit))}`,
    )
  }

  /**
   * List every session for a specific user.
   *
   * @param userId The user whose sessions to list.
   * @param includeRevoked When true, revoked sessions are also returned
   *   with their ``revoked`` and ``revoked_reason`` fields populated.
   */
  async listUserSessions(
    userId: string,
    includeRevoked: boolean = false,
  ): Promise<ISessionInfo[]> {
    const qs = includeRevoked ? '?include_revoked=true' : ''
    return this.request<ISessionInfo[]>(
      `/admin/users/${encodeURIComponent(userId)}/sessions${qs}`,
    )
  }

  /**
   * Force-revoke a session of a specific user.
   *
   * The endpoint returns ``{ revoked: boolean }`` — ``false`` means the
   * session was already revoked (no-op).  The server returns 404 when
   * the session does not exist or does not belong to ``userId``.
   */
  async revokeUserSession(
    userId: string,
    sid: string,
  ): Promise<{ revoked: boolean }> {
    return this.request<{ revoked: boolean }>(
      `/admin/users/${encodeURIComponent(userId)}/sessions/${encodeURIComponent(sid)}`,
      { method: 'DELETE' },
    )
  }

  // -----------------------------------------------------------------------
  // Departments
  // -----------------------------------------------------------------------

  async listDepartments(): Promise<IDepartment[]> {
    return this.request<IDepartment[]>('/admin/departments')
  }

  async createDepartment(name: string, description: string): Promise<IDepartment> {
    return this.request<IDepartment>('/admin/departments', {
      method: 'POST',
      body: JSON.stringify({ name, description }),
    })
  }

  async getDepartment(id: string): Promise<IDepartment> {
    return this.request<IDepartment>(`/admin/departments/${encodeURIComponent(id)}`)
  }

  async updateDepartment(
    id: string,
    data: { name?: string; description?: string },
  ): Promise<IDepartment> {
    return this.request<IDepartment>(`/admin/departments/${encodeURIComponent(id)}`, {
      method: 'PATCH',
      body: JSON.stringify(data),
    })
  }

  async deleteDepartment(id: string): Promise<void> {
    await this.request<{ deleted: boolean }>(
      `/admin/departments/${encodeURIComponent(id)}`,
      { method: 'DELETE' },
    )
  }

  async disableDepartment(id: string): Promise<void> {
    await this.request<IDepartment>(
      `/admin/departments/${encodeURIComponent(id)}/disable`,
      { method: 'POST' },
    )
  }

  async enableDepartment(id: string): Promise<void> {
    await this.request<IDepartment>(
      `/admin/departments/${encodeURIComponent(id)}/enable`,
      { method: 'POST' },
    )
  }

  async bindSkill(deptId: string, skillName: string): Promise<void> {
    await this.request(
      `/admin/departments/${encodeURIComponent(deptId)}/skills/${encodeURIComponent(skillName)}`,
      { method: 'POST' },
    )
  }

  async unbindSkill(deptId: string, skillName: string): Promise<void> {
    await this.request(
      `/admin/departments/${encodeURIComponent(deptId)}/skills/${encodeURIComponent(skillName)}`,
      { method: 'DELETE' },
    )
  }

  async bindKb(deptId: string, sourceId: string): Promise<void> {
    await this.request(
      `/admin/departments/${encodeURIComponent(deptId)}/kb/${encodeURIComponent(sourceId)}`,
      { method: 'POST' },
    )
  }

  async unbindKb(deptId: string, sourceId: string): Promise<void> {
    await this.request(
      `/admin/departments/${encodeURIComponent(deptId)}/kb/${encodeURIComponent(sourceId)}`,
      { method: 'DELETE' },
    )
  }

  async listDepartmentSkills(deptId: string): Promise<string[]> {
    return this.request<string[]>(
      `/admin/departments/${encodeURIComponent(deptId)}/skills`,
    )
  }

  async listDepartmentKb(deptId: string): Promise<string[]> {
    return this.request<string[]>(
      `/admin/departments/${encodeURIComponent(deptId)}/kb`,
    )
  }

  // -----------------------------------------------------------------------
  // Users
  // -----------------------------------------------------------------------

  async listUsers(departmentId?: string): Promise<IAdminUser[]> {
    const qs = departmentId
      ? `?department_id=${encodeURIComponent(departmentId)}`
      : ''
    return this.request<IAdminUser[]>(`/admin/users${qs}`)
  }

  async createUser(data: {
    username: string
    email: string
    password: string
    role?: string
    department_ids?: string[]
  }): Promise<IAdminUser> {
    return this.request<IAdminUser>('/admin/users', {
      method: 'POST',
      body: JSON.stringify(data),
    })
  }

  async getUser(id: string): Promise<IAdminUser> {
    return this.request<IAdminUser>(`/admin/users/${encodeURIComponent(id)}`)
  }

  async updateUser(id: string, data: Partial<IAdminUser>): Promise<IAdminUser> {
    return this.request<IAdminUser>(`/admin/users/${encodeURIComponent(id)}`, {
      method: 'PATCH',
      body: JSON.stringify(data),
    })
  }

  async deleteUser(id: string): Promise<void> {
    await this.request<{ deleted: boolean }>(
      `/admin/users/${encodeURIComponent(id)}`,
      { method: 'DELETE' },
    )
  }

  async resetPassword(id: string, newPassword: string): Promise<void> {
    await this.request<{ reset: boolean }>(
      `/admin/users/${encodeURIComponent(id)}/reset-password`,
      {
        method: 'POST',
        body: JSON.stringify({ new_password: newPassword }),
      },
    )
  }

  async assignDepartment(userId: string, deptId: string): Promise<void> {
    await this.request<{ assigned: boolean }>(
      `/admin/users/${encodeURIComponent(userId)}/departments/${encodeURIComponent(deptId)}`,
      { method: 'POST' },
    )
  }

  async removeDepartment(userId: string, deptId: string): Promise<void> {
    await this.request<{ removed: boolean }>(
      `/admin/users/${encodeURIComponent(userId)}/departments/${encodeURIComponent(deptId)}`,
      { method: 'DELETE' },
    )
  }

  // -----------------------------------------------------------------------
  // LLM Config
  // -----------------------------------------------------------------------

  async listLlmProviders(): Promise<ILlmProvider[]> {
    return this.request<ILlmProvider[]>('/admin/llm/providers')
  }

  async createLlmProvider(
    name: string,
    config: Record<string, unknown>,
  ): Promise<ILlmProvider> {
    return this.request<ILlmProvider>('/admin/llm/providers', {
      method: 'POST',
      body: JSON.stringify({ name, ...config }),
    })
  }

  async updateLlmProvider(
    name: string,
    config: Record<string, unknown>,
  ): Promise<ILlmProvider> {
    return this.request<ILlmProvider>(
      `/admin/llm/providers/${encodeURIComponent(name)}`,
      {
        method: 'PATCH',
        body: JSON.stringify(config),
      },
    )
  }

  async deleteLlmProvider(name: string): Promise<void> {
    await this.request<{ deleted: boolean }>(
      `/admin/llm/providers/${encodeURIComponent(name)}`,
      { method: 'DELETE' },
    )
  }

  async setLlmApiKey(name: string, apiKey: string): Promise<void> {
    await this.request(
      `/admin/llm/providers/${encodeURIComponent(name)}/api-key`,
      {
        method: 'POST',
        body: JSON.stringify({ api_key: apiKey }),
      },
    )
  }

  async listLlmFallbacks(): Promise<Record<string, string[]>> {
    return this.request<Record<string, string[]>>('/admin/llm/fallbacks')
  }

  async setLlmFallback(model: string, chain: string[]): Promise<void> {
    await this.request(
      `/admin/llm/fallbacks/${encodeURIComponent(model)}`,
      {
        method: 'PUT',
        body: JSON.stringify({ chain }),
      },
    )
  }

  async deleteLlmFallback(model: string): Promise<void> {
    await this.request<{ deleted: boolean }>(
      `/admin/llm/fallbacks/${encodeURIComponent(model)}`,
      { method: 'DELETE' },
    )
  }

  // -----------------------------------------------------------------------
  // Quotas
  // -----------------------------------------------------------------------

  async listDepartmentQuotas(deptId: string): Promise<IQuota[]> {
    return this.request<IQuota[]>(
      `/admin/departments/${encodeURIComponent(deptId)}/quotas`,
    )
  }

  async setDepartmentQuota(
    deptId: string,
    data: { quota_type: string; limit_value: number | string[]; period: string },
  ): Promise<IQuota> {
    return this.request<IQuota>(
      `/admin/departments/${encodeURIComponent(deptId)}/quotas`,
      {
        method: 'PUT',
        body: JSON.stringify(data),
      },
    )
  }

  async deleteDepartmentQuota(
    deptId: string,
    quotaType: string,
    period: string,
  ): Promise<void> {
    const qs = `?quota_type=${encodeURIComponent(quotaType)}&period=${encodeURIComponent(period)}`
    await this.request<{ deleted: boolean }>(
      `/admin/departments/${encodeURIComponent(deptId)}/quotas${qs}`,
      { method: 'DELETE' },
    )
  }

  // -----------------------------------------------------------------------
  // Skills
  // -----------------------------------------------------------------------

  async enableSkill(name: string): Promise<void> {
    await this.request(
      `/admin/skills/${encodeURIComponent(name)}/enable`,
      { method: 'POST' },
    )
  }

  async disableSkill(name: string): Promise<void> {
    await this.request(
      `/admin/skills/${encodeURIComponent(name)}/disable`,
      { method: 'POST' },
    )
  }

  async importSkill(yamlContent: string): Promise<void> {
    await this.request('/admin/skills/import', {
      method: 'POST',
      body: JSON.stringify({ yaml_content: yamlContent }),
    })
  }

  async reloadSkill(name: string): Promise<void> {
    await this.request(
      `/admin/skills/${encodeURIComponent(name)}/reload`,
      { method: 'POST' },
    )
  }

  async updateSkillConfig(
    name: string,
    config: Record<string, unknown>,
  ): Promise<void> {
    await this.request(`/admin/skills/${encodeURIComponent(name)}`, {
      method: 'PATCH',
      body: JSON.stringify({ config }),
    })
  }

  // -----------------------------------------------------------------------
  // KB
  // -----------------------------------------------------------------------

  async listKbDocuments(
    sourceId?: string,
    departmentId?: string,
  ): Promise<IKbDocument[]> {
    const params: string[] = []
    if (sourceId) params.push(`source_id=${encodeURIComponent(sourceId)}`)
    if (departmentId) params.push(`department_id=${encodeURIComponent(departmentId)}`)
    const qs = params.length ? `?${params.join('&')}` : ''
    const res = await this.request<{ documents: IKbDocument[] }>(
      `/admin/kb/documents${qs}`,
    )
    return res?.documents ?? []
  }

  async uploadKbDocument(data: {
    filename: string
    content: string
    source_id: string
    department_id?: string
  }): Promise<IKbDocument> {
    return this.request<IKbDocument>('/admin/kb/documents', {
      method: 'POST',
      body: JSON.stringify(data),
    })
  }

  async deleteKbDocument(id: string): Promise<void> {
    await this.request<{ deleted: boolean }>(
      `/admin/kb/documents/${encodeURIComponent(id)}`,
      { method: 'DELETE' },
    )
  }

  async syncKbSource(id: string): Promise<void> {
    await this.request(
      `/admin/kb/sources/${encodeURIComponent(id)}/sync`,
      { method: 'POST' },
    )
  }

  async rebuildKbSource(id: string): Promise<void> {
    await this.request(
      `/admin/kb/sources/${encodeURIComponent(id)}/rebuild`,
      { method: 'POST' },
    )
  }

  // -----------------------------------------------------------------------
  // Usage
  // -----------------------------------------------------------------------

  async getUsageSummary(params: {
    department_id?: string
    user_id?: string
    start?: string
    end?: string
  }): Promise<IUsageSummary> {
    return this.request<IUsageSummary>(
      `/admin/usage/summary${_buildUsageQuery(params)}`,
    )
  }

  async getUsageTimeseries(params: {
    department_id?: string
    user_id?: string
    start?: string
    end?: string
    interval?: string
  }): Promise<IUsageTimeseries[]> {
    const qs = _buildUsageQuery(params, params.interval ? { interval: params.interval } : {})
    return this.request<IUsageTimeseries[]>(`/admin/usage/timeseries${qs}`)
  }

  async getUsageByModel(params: {
    department_id?: string
    user_id?: string
    start?: string
    end?: string
  }): Promise<IUsageByModel[]> {
    return this.request<IUsageByModel[]>(
      `/admin/usage/by-model${_buildUsageQuery(params)}`,
    )
  }

  async getUsageTopUsers(params: {
    department_id?: string
    limit?: number
  }): Promise<IUsageTopUser[]> {
    const extra: Record<string, string> = {}
    if (params.limit !== undefined) extra.limit = String(params.limit)
    return this.request<IUsageTopUser[]>(
      `/admin/usage/top-users${_buildUsageQuery(params, extra)}`,
    )
  }

  async exportUsage(params: {
    department_id?: string
    user_id?: string
    start?: string
    end?: string
    format?: string
  }): Promise<string> {
    const extra: Record<string, string> = {}
    if (params.format) extra.format = params.format
    return this.request<string>(
      `/admin/usage/export${_buildUsageQuery(params, extra)}`,
    )
  }
}

/**
 * Build a query string from the common usage filter params.
 *
 * Undefined / empty values are omitted. ``extra`` is merged in for
 * endpoint-specific params (``interval``, ``limit``, ``format``).
 */
function _buildUsageQuery(
  params: { department_id?: string; user_id?: string; start?: string; end?: string },
  extra: Record<string, string> = {},
): string {
  const parts: string[] = []
  if (params.department_id) parts.push(`department_id=${encodeURIComponent(params.department_id)}`)
  if (params.user_id) parts.push(`user_id=${encodeURIComponent(params.user_id)}`)
  if (params.start) parts.push(`start=${encodeURIComponent(params.start)}`)
  if (params.end) parts.push(`end=${encodeURIComponent(params.end)}`)
  for (const [k, v] of Object.entries(extra)) {
    parts.push(`${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
  }
  return parts.length ? `?${parts.join('&')}` : ''
}

export const adminApi = new AdminApiClient()
export { AdminApiClient }
