import type { TokenResponse } from '@/types/auth';

const AUTHZ_API_BASE_URL = (process.env.AUTHZ_API_BASE_URL || 'http://127.0.0.1:19090').trim().replace(/\/+$/, '');
const DEPLOY_API_BASE_URL = (process.env.DEPLOY_API_BASE_URL || 'http://127.0.0.1:8090').trim().replace(/\/+$/, '');
const DEPLOY_API_TOKEN = (process.env.DEPLOY_API_TOKEN || '').trim();
const REQUEST_TIMEOUT_MS = 15000;
const REGISTER_REQUEST_TIMEOUT_MS = 90000;

type JsonObject = Record<string, unknown>;

export class HttpError extends Error {
  status: number;

  constructor(status: number, message: string) {
    super(message);
    this.status = status;
  }
}

function asObject(value: unknown): JsonObject {
  return value && typeof value === 'object' && !Array.isArray(value) ? (value as JsonObject) : {};
}

function asString(value: unknown): string {
  return typeof value === 'string' ? value.trim() : '';
}

async function fetchJson<T>(url: string, init?: RequestInit, timeoutMs = REQUEST_TIMEOUT_MS): Promise<T> {
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);

  try {
    const response = await fetch(url, {
      ...init,
      headers: {
        'Content-Type': 'application/json',
        ...(init?.headers || {}),
      },
      cache: 'no-store',
      signal: controller.signal,
    });

    const raw = await response.text();
    let payload: unknown = {};
    if (raw) {
      try {
        payload = JSON.parse(raw);
      } catch {
        payload = { detail: raw };
      }
    }

    if (!response.ok) {
      const detail = asString(asObject(payload).detail) || `request failed with status ${response.status}`;
      throw new HttpError(response.status, detail);
    }

    return payload as T;
  } catch (error) {
    if (error instanceof HttpError) {
      throw error;
    }
    if (error instanceof DOMException && error.name === 'AbortError') {
      throw new HttpError(504, 'request timed out');
    }
    throw new HttpError(502, error instanceof Error ? error.message : 'request failed');
  } finally {
    clearTimeout(timeoutId);
  }
}

export async function callDeployControl<T>(path: string, payload: JsonObject, timeoutMs = REQUEST_TIMEOUT_MS): Promise<T> {
  const headers: Record<string, string> = {};
  if (DEPLOY_API_TOKEN) {
    headers.Authorization = `Bearer ${DEPLOY_API_TOKEN}`;
  }
  return fetchJson<T>(`${DEPLOY_API_BASE_URL}${path}`, {
    method: 'POST',
    headers,
    body: JSON.stringify(payload),
  }, timeoutMs);
}

export async function callAuthzService<T>(path: string, payload: JsonObject, timeoutMs = REQUEST_TIMEOUT_MS): Promise<T> {
  return fetchJson<T>(`${AUTHZ_API_BASE_URL}${path}`, {
    method: 'POST',
    body: JSON.stringify(payload),
  }, timeoutMs);
}

export { REGISTER_REQUEST_TIMEOUT_MS };

export async function callInstanceApi<T>(apiBaseUrl: string, path: string, payload: JsonObject): Promise<T> {
  const baseUrl = apiBaseUrl.trim().replace(/\/+$/, '');
  if (!baseUrl) {
    throw new HttpError(500, 'instance api base url is missing');
  }
  return fetchJson<T>(`${baseUrl}${path}`, {
    method: 'POST',
    body: JSON.stringify(payload),
  });
}

export function normalizeTokenResponse(
  response: TokenResponse,
  routing: {
    frontend_base_url?: unknown;
    api_base_url?: unknown;
    public_url?: unknown;
  }
): TokenResponse {
  const frontendBaseUrl = asString(routing.frontend_base_url);
  const apiBaseUrl = asString(routing.api_base_url) || asString(routing.public_url);
  const publicUrl = asString(routing.public_url) || apiBaseUrl;
  const backendConnection = asObject(response.backend_connection);

  const mergedBackendConnection = {
    ...backendConnection,
    frontend_base_url: asString(backendConnection.frontend_base_url) || frontendBaseUrl || publicUrl || null,
    api_base_url: asString(backendConnection.api_base_url) || apiBaseUrl || publicUrl || null,
    public_base_url: asString(backendConnection.public_base_url) || publicUrl || apiBaseUrl || null,
  };

  return {
    ...response,
    backend_connection: mergedBackendConnection,
  };
}
