import type {
  Account,
  ClsNewsDocument,
  DailyInputDocument,
  DailyInputUpsertPayload,
  ReportDocument,
  ReportListItem,
} from '../types'

const API_BASE = import.meta.env.VITE_API_BASE_URL ?? 'http://127.0.0.1:3000'

export class ApiError extends Error {
  status: number

  constructor(message: string, status: number) {
    super(message)
    this.name = 'ApiError'
    this.status = status
  }
}

function sleep(ms: number) {
  return new Promise((resolve) => {
    window.setTimeout(resolve, ms)
  })
}

async function request<T>(path: string, init?: RequestInit): Promise<T> {
  const method = init?.method ?? 'GET'
  const execute = async () => {
    const response = await fetch(`${API_BASE}${path}`, {
      headers: {
        'Content-Type': 'application/json',
        ...(init?.headers ?? {}),
      },
      ...init,
    })

    if (!response.ok) {
      const text = await response.text()
      throw new ApiError(text || `Request failed with status ${response.status}`, response.status)
    }

    return response.json() as Promise<T>
  }

  try {
    return await execute()
  } catch (error) {
    if (method === 'GET') {
      await sleep(250)
      return execute()
    }
    throw error
  }
}

function withDateQuery(path: string, date?: string): string {
  if (!date) {
    return path
  }
  const separator = path.includes('?') ? '&' : '?'
  return `${path}${separator}date=${encodeURIComponent(date)}`
}

export const api = {
  getAccounts() {
    return request<Account[]>('/api/accounts')
  },
  getClsNews(date?: string) {
    return request<ClsNewsDocument>(withDateQuery('/api/cls-news', date))
  },
  refreshClsNews(date?: string) {
    return request<ClsNewsDocument>(withDateQuery('/api/cls-news/refresh', date), { method: 'POST' })
  },
  getDailyInput(date: string) {
    return request<DailyInputDocument>(`/api/daily-inputs/${date}`)
  },
  saveDailyInput(date: string, payload: DailyInputUpsertPayload) {
    return request<DailyInputDocument>(`/api/daily-inputs/${date}`, {
      method: 'PUT',
      body: JSON.stringify(payload),
    })
  },
  getOpinionReport(date: string) {
    return request<ReportDocument>(`/api/opinions/${date}`)
  },
  generateOpinionReport(date: string) {
    return request<ReportDocument>(`/api/reports/${date}/generate`, {
      method: 'POST',
    })
  },
  getReportList() {
    return request<ReportListItem[]>('/api/reports')
  },
}
