interface TokenResponse {
  access_token: string
  expires_in: number
  token_type: string
  scope: string | null
}

let cachedToken: string | null = null
let tokenExpiresAt = 0

const getToken = async (): Promise<string> => {
  if (cachedToken && Date.now() < tokenExpiresAt) {
    return cachedToken
  }

  try {
    const response = await fetch('https://api.rentsyst.com/v2/oauth2/token', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        client_id: process.env.NEXT_PUBLIC_API_CLIENT_ID || '',
        client_secret: process.env.NEXT_PUBLIC_API_SECRET_ID || '',
        grant_type: 'client_credentials',
        scope: 'booking',
      }),
    })

    if (!response.ok) {
      const errorBody = await response.text()
      throw new Error(
        `Failed to get access token: ${response.status} ${response.statusText} - ${errorBody}`,
      )
    }

    const tokenData: TokenResponse = await response.json()
    cachedToken = tokenData.access_token
    tokenExpiresAt = Date.now() + (tokenData.expires_in - 60) * 1000
    return cachedToken
  } catch (error) {
    console.error('Error getting access token:', error)
    throw error
  }
}

type CustomFetchOptions = Omit<RequestInit, 'method' | 'body'> & {
  body?: any
}

export const customFetch = async (
  url: string,
  isNeedAuth: boolean,
  method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH',
  options: CustomFetchOptions = {},
): Promise<Response> => {
  const headers = new Headers(options.headers)
  const isFormData = options.body instanceof FormData
  if (!isFormData && !headers.has('Content-Type')) {
    headers.set('Content-Type', 'application/json')
  }

  if (isNeedAuth) {
    try {
      const token = await getToken()
      headers.set('Authorization', `Bearer ${token}`)
    } catch (error) {
      return Promise.reject(error)
    }
  }

  const requestOptions: RequestInit = {
    ...options,
    method,
    headers,
  }

  if (options.body) {
    requestOptions.body = isFormData ? options.body : JSON.stringify(options.body)
  }

  try {
    const response = await fetch(url, requestOptions)

    if (!response.ok) {
      const errorBody = await response.text()
      throw new Error(
        `API request failed: ${response.status} ${response.statusText} - ${errorBody}`,
      )
    }

    return response
  } catch (error) {
    console.error('API request error:', error)
    throw error
  }
}
