// Мини-клиент API. Ошибки авторизации редиректят на вход.
import i18n from './i18n'

export interface Tag {
  id: number
  name: string
}

export interface Project {
  id: number
  name: string
  relevance_status: string
  priority: number | null
  note: string
}

// Черновое предложение автодетализации (LLM)
export interface AiProposal {
  tags: string[]
  project: string | null
  new_project: boolean
  priority: number | null
  estimated_minutes?: number | null
}

export interface Attachment {
  id: number
  task_id: number
  original_name: string
  mime: string
  size: number
  created_at: string
}

export interface Task {
  id: number
  title: string
  description: string
  task_type: string
  status: string
  detail_state: string
  parent_task_id: number | null
  project: Project | null
  priority: number | null
  tags: Tag[]
  ai_proposal: AiProposal | null
  estimated_minutes: number | null
  actual_minutes: number | null
  budget_money: number | null
  cost_estimate_money: number | null
  deadline_date: string | null
  deadline_period: string | null
  recur_kind: string | null
  recur_interval_days: number | null
  recur_weekdays: string | null
  recur_day_of_month: number | null
  /** «Ментально сложная» (ТЗ 3.13): метка «не хочется делать», +10 XP при закрытии */
  mentally_hard: boolean
  created_at: string
  approved_at: string | null
  done_at: string | null
}

export interface TaskUpdateInput {
  title?: string
  description?: string
  project_id?: number | null
  parent_task_id?: number | null
  tag_ids?: number[]
  priority?: number | null
  status?: string
  estimated_minutes?: number | null
  actual_minutes?: number | null
  budget_money?: number | null
  cost_estimate_money?: number | null
  deadline_date?: string | null
  deadline_period?: string | null
  task_type?: string
  recur_kind?: string | null
  recur_interval_days?: number | null
  recur_weekdays?: string | null
  recur_day_of_month?: number | null
  mentally_hard?: boolean
  /** Не поле задачи: источник закрытия для геймификации ("options" — режим «3 вариантов») */
  earned_via?: string
}

export interface XpSummary {
  total_xp: number
  level: number
  level_xp: number
  level_span: number
  level_percent: number
  completed_count: number
}

export interface XpEvent {
  task_id: number | null
  amount: number
  rarity: string
  via_options: boolean
  created_at: string
}

export interface AppSettings {
  currency: string
  /** '' = нет переопределения, язык берётся из SSO locale */
  language: string
}

export type AppSettingsUpdate = Partial<AppSettings>

export class ApiError extends Error {
  status: number
  constructor(status: number, message: string) {
    super(message)
    this.status = status
  }
}

async function request<T>(path: string, options: RequestInit = {}): Promise<T> {
  // Content-Type не задаётся для FormData — браузер проставит boundary сам.
  const headers: Record<string, string> = {}
  if (!(options.body instanceof FormData)) headers['Content-Type'] = 'application/json'
  const res = await fetch(path, { headers, ...options })
  if (res.status === 401) {
    window.location.href = '/auth/login'
    throw new ApiError(401, 'Not authenticated')
  }
  if (!res.ok) {
    const body = await res.json().catch(() => ({ detail: res.statusText }))
    throw new ApiError(res.status, body.detail ?? i18n.global.t('common.error'))
  }
  // Геймификация: бэк помечает закрытие задачи заголовками (XP, редкость, «3 варианта»,
  // «ментально сложная»)
  const earned = res.headers.get('X-Earned-XP')
  if (earned) {
    earnedXpBuffer = {
      xp: Number(earned) || 0,
      rarity: res.headers.get('X-Plant-Rarity') ?? 'common',
      options: res.headers.get('X-Options-Close') === '1',
      hard: res.headers.get('X-Mentally-Hard') === '1',
    }
  }
  return res.json() as Promise<T>
}

// Геймификация последнего закрывающего запроса (забирается через takeEarnedXp)
export interface EarnedReward {
  xp: number
  rarity: string // common | rare | epic
  options: boolean // закрыто из режима «3 вариантов»
  hard: boolean // «ментально сложная» — награда за силу воли
}

let earnedXpBuffer: EarnedReward | null = null

export function takeEarnedXp(): EarnedReward | null {
  const value = earnedXpBuffer
  earnedXpBuffer = null
  return value
}

export const api = {
  // tasks
  listTasks: (params: Record<string, string | number | null | undefined> = {}) => {
    const qs = new URLSearchParams()
    Object.entries(params).forEach(([k, v]) => {
      if (v !== undefined && v !== null && v !== '') qs.set(k, String(v))
    })
    return request<Task[]>('/api/tasks?' + qs)
  },
  getTask: (id: number) => request<Task>(`/api/tasks/${id}`),
  createTask: (title: string, description = '', parentTaskId?: number) =>
    request<{ id: number }>('/api/tasks', {
      method: 'POST',
      body: JSON.stringify({
        title,
        description,
        ...(parentTaskId !== undefined ? { parent_task_id: parentTaskId } : {}),
      }),
    }),
  updateTask: (id: number, patch: TaskUpdateInput) =>
    request<Task>(`/api/tasks/${id}`, { method: 'PATCH', body: JSON.stringify(patch) }),
  approveTask: (id: number, applyProposal = false) =>
    request<Task>(`/api/tasks/${id}/approve`, {
      method: 'POST',
      body: JSON.stringify({ apply_proposal: applyProposal }),
    }),
  redetailTask: (id: number) =>
    request<Task>(`/api/tasks/${id}/redetail`, { method: 'POST' }),
  suggestTasks: (availableMinutes: number) =>
    request<Task[]>('/api/tasks/suggest', {
      method: 'POST',
      body: JSON.stringify({ available_minutes: availableMinutes }),
    }),
  deleteTask: (id: number) => request<{ ok: boolean }>(`/api/tasks/${id}`, { method: 'DELETE' }),
  // attachments
  listAttachments: (taskId: number) =>
    request<Attachment[]>(`/api/tasks/${taskId}/attachments`),
  uploadAttachments: (taskId: number, files: File[]) => {
    const form = new FormData()
    files.forEach((f) => form.append('files', f))
    return request<Attachment[]>(`/api/tasks/${taskId}/attachments`, {
      method: 'POST',
      body: form,
    })
  },
  deleteAttachment: (id: number) =>
    request<{ ok: boolean }>(`/api/attachments/${id}`, { method: 'DELETE' }),
  // Ссылка на файл вложения (нужна сессионная cookie — просто <img src>)
  attachmentUrl: (id: number) => `/api/attachments/${id}/file`,
  // projects
  listProjects: () => request<Project[]>('/api/projects'),
  getProject: (id: number) => request<Project>(`/api/projects/${id}`),
  createProject: (name: string) =>
    request<Project>('/api/projects', { method: 'POST', body: JSON.stringify({ name }) }),
  updateProject: (id: number, patch: Partial<Omit<Project, 'id'>>) =>
    request<Project>(`/api/projects/${id}`, { method: 'PATCH', body: JSON.stringify(patch) }),
  deleteProject: (id: number) =>
    request<{ ok: boolean }>(`/api/projects/${id}`, { method: 'DELETE' }),
  // tags
  listTags: () => request<Tag[]>('/api/tags'),
  createTag: (name: string) =>
    request<Tag>('/api/tags', { method: 'POST', body: JSON.stringify({ name }) }),
  // settings (глобальные: валюта бюджетов — выбирается один раз)
  getSettings: () => request<AppSettings>('/api/settings'),
  updateSettings: (patch: AppSettingsUpdate) =>
    request<AppSettings>('/api/settings', { method: 'PUT', body: JSON.stringify(patch) }),
  // геймификация (ТЗ 3.13)
  getXp: () => request<XpSummary>('/api/xp'),
  getXpEvents: () => request<XpEvent[]>('/api/xp/events'),
}