const BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:5270'

let authToken = null
let refreshToken = null
let refreshPromise = null
let onSessionExpired = null

export function setAuthToken(token) {
  authToken = token
}

export function setRefreshToken(token) {
  refreshToken = token
}

export function onSessionExpiredCallback(callback) {
  onSessionExpired = callback
}

function authHeaders(extra = {}) {
  const headers = { 'Content-Type': 'application/json', ...extra }
  if (authToken) headers['Authorization'] = `Bearer ${authToken}`
  return headers
}

async function attemptRefresh() {
  if (!refreshToken) return false
  if (refreshPromise) return refreshPromise

  refreshPromise = (async () => {
    try {
      const res = await fetch(`${BASE_URL}/api/v1/auth/refresh`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ refreshToken }),
      })
      if (!res.ok) return false
      const envelope = await res.json()
      if (!envelope.success) return false

      authToken = envelope.data.accessToken
      refreshToken = envelope.data.refreshToken
      if (onSessionExpired) {
        onSessionExpired({
          type: 'refreshed',
          accessToken: envelope.data.accessToken,
          refreshToken: envelope.data.refreshToken,
          expiresAt: envelope.data.expiresAt,
        })
      }
      return true
    } catch {
      return false
    } finally {
      refreshPromise = null
    }
  })()

  return refreshPromise
}

function handleSessionExpired() {
  if (onSessionExpired) {
    onSessionExpired({ type: 'expired' })
  }
}

async function request(path, options = {}) {
  const res = await fetch(`${BASE_URL}${path}`, {
    headers: authHeaders(options.headers),
    ...options,
  })
  if (res.status === 401) {
    const refreshed = await attemptRefresh()
    if (refreshed) {
      const retry = await fetch(`${BASE_URL}${path}`, {
        headers: authHeaders(options.headers),
        ...options,
      })
      const retryEnvelope = await retry.json()
      if (!retry.ok || !retryEnvelope.success) {
        const msg = retryEnvelope.error?.message ?? `API ${retry.status}: ${path}`
        throw new Error(msg)
      }
      return retryEnvelope.data
    }
    handleSessionExpired()
    throw new Error('Session expired — please log in again.')
  }
  const envelope = await res.json()
  if (!res.ok || !envelope.success) {
    const msg = envelope.error?.message ?? `API ${res.status}: ${path}`
    throw new Error(msg)
  }
  return envelope.data
}

async function requestOptional(path) {
  const res = await fetch(`${BASE_URL}${path}`, {
    headers: authHeaders(),
  })
  if (res.status === 404) return null
  if (res.status === 401) {
    const refreshed = await attemptRefresh()
    if (refreshed) {
      const retry = await fetch(`${BASE_URL}${path}`, {
        headers: authHeaders(),
      })
      if (retry.status === 404) return null
      const retryEnvelope = await retry.json()
      if (!retry.ok || !retryEnvelope.success) {
        const msg = retryEnvelope.error?.message ?? `API ${retry.status}: ${path}`
        throw new Error(msg)
      }
      return retryEnvelope.data ?? null
    }
    handleSessionExpired()
    throw new Error('Session expired — please log in again.')
  }
  const envelope = await res.json()
  if (!res.ok || !envelope.success) {
    const msg = envelope.error?.message ?? `API ${res.status}: ${path}`
    throw new Error(msg)
  }
  return envelope.data ?? null
}

export const api = {
  get: (path) => request(path),
  getOptional: (path) => requestOptional(path),
  post: (path, body) => request(path, {
    method: 'POST',
    body: body !== undefined ? JSON.stringify(body) : undefined,
  }),
  put: (path, body) => request(path, {
    method: 'PUT',
    body: body !== undefined ? JSON.stringify(body) : undefined,
  }),
  patch: (path, body) => request(path, {
    method: 'PATCH',
    body: body !== undefined ? JSON.stringify(body) : undefined,
  }),
  delete: async (path) => {
    const res = await fetch(`${BASE_URL}${path}`, {
      method: 'DELETE',
      headers: authHeaders(),
    })
    if (res.status === 401) {
      const refreshed = await attemptRefresh()
      if (refreshed) {
        const retry = await fetch(`${BASE_URL}${path}`, {
          method: 'DELETE',
          headers: authHeaders(),
        })
        if (retry.status === 204) return null
        const retryEnvelope = await retry.json()
        if (!retry.ok || !retryEnvelope.success) {
          const msg = retryEnvelope.error?.message ?? `API ${retry.status}: ${path}`
          throw new Error(msg)
        }
        return retryEnvelope.data ?? null
      }
      handleSessionExpired()
      throw new Error('Session expired — please log in again.')
    }
    if (res.status === 204) return null
    const envelope = await res.json()
    if (!res.ok || !envelope.success) {
      const msg = envelope.error?.message ?? `API ${res.status}: ${path}`
      throw new Error(msg)
    }
    return envelope.data ?? null
  },
}

export { BASE_URL, authHeaders }
