import api from './api';
import type {
  ApiTokenRecord,
  AssetResponse,
  CapabilityResponse,
  CapabilitySetResponse,
  DownloadLink,
  DocumentResponse,
  FolderTreeNode,
  Identifier,
  PasskeySummary,
  TenantSnippet,
  TagResponse,
} from './apiTypes';
import type { AxiosError, AxiosInstance, InternalAxiosRequestConfig, AxiosRequestConfig } from 'axios';

export const httpClient: Pick<AxiosInstance, 'get' | 'post' | 'patch' | 'delete' | 'defaults'> = {
  get: api.get.bind(api),
  post: api.post.bind(api),
  patch: api.patch.bind(api),
  delete: api.delete.bind(api),
  defaults: api.defaults,
};

type AuthAwareRequestConfig = InternalAxiosRequestConfig & {
  _retry?: boolean;
  skipAuthRefresh?: boolean;
};

type AuthRequestConfig = AxiosRequestConfig & {
  skipAuthRefresh?: boolean;
};
type AuthRefreshHandlers = {
  onRefreshSuccess?: (token: string, payload?: { tenant?: unknown }) => void;
  onRefreshFailure?: (error: unknown) => void;
};

let refreshPromise: Promise<string> | null = null;
let authRefreshHandlers: AuthRefreshHandlers = {};

const normalizeNumber = (value: unknown): number | undefined => {
  const n = Number(value);
  return Number.isFinite(n) ? n : undefined;
};

const normalizeDownload = (input?: DownloadLink | null): DownloadLink | null => {
  if (!input?.url) {
    return null;
  }
  const expires_at = normalizeNumber(input.expires_at);
  if (!expires_at) {
    return null;
  }
  return { url: input.url, expires_at };
};

export const fetchDocument = async (id: Identifier): Promise<DocumentResponse> => {
  const { data } = await api.get<{ document?: DocumentResponse }>(`/documents/${id}`);
  const doc = data?.document || (data as unknown as DocumentResponse);
  if (doc?.current_version?.download) {
    doc.current_version.download = normalizeDownload(doc.current_version.download);
  }
  return doc;
};

export const fetchAsset = async (id: Identifier): Promise<AssetResponse> => {
  const { data } = await api.get<AssetResponse>(`/assets/${id}`);
  const download = normalizeDownload(data.download);
  return {
    ...data,
    download,
  };
};

export const listDocuments = async (params: Record<string, unknown> = {}): Promise<DocumentResponse[]> => {
  const { data } = await api.get<DocumentResponse[]>('/documents', { params });
  return Array.isArray(data) ? data : [];
};

export const getFolderTree = async (): Promise<FolderTreeNode[]> => {
  const { data } = await api.get<FolderTreeNode[]>('/folders/tree');
  return Array.isArray(data) ? data : [];
};

export const listCapabilitySets = async (): Promise<CapabilitySetResponse[]> => {
  const { data } = await api.get<CapabilitySetResponse[]>('/capability-sets');
  return Array.isArray(data) ? data : [];
};

export const listCapabilities = async (): Promise<CapabilityResponse[]> => {
  const { data } = await api.get<CapabilityResponse[]>('/capabilities');
  return Array.isArray(data) ? data : [];
};

export const listApiTokens = async (): Promise<ApiTokenRecord[]> => {
  const { data } = await api.get<ApiTokenRecord[]>('/profile/api-tokens');
  return Array.isArray(data) ? data : [];
};

export const listPasskeys = async (): Promise<PasskeySummary[]> => {
  const { data } = await api.get<PasskeySummary[]>('/profile/passkeys');
  return Array.isArray(data) ? data : [];
};

export const moveDocumentsBulk = async (documentIds: Identifier[], folderId: Identifier | null): Promise<void> => {
  await api.post('/documents/bulk/move', {
    document_ids: documentIds,
    folder_id: folderId,
  });
};

export const queueDocumentReanalysis = async (
  documentId: Identifier,
  options: { force?: boolean } = {},
): Promise<void> => {
  const { force = false } = options;
  await api.post(`/documents/${documentId}/assets`, null, { params: { force } });
};

export const trashDocument = async (documentId: Identifier): Promise<void> => {
  await api.post(`/documents/${documentId}/trash`);
};

export const addDocumentTags = async (documentId: Identifier, tagIds: Identifier[]): Promise<void> => {
  await api.post(`/documents/${documentId}/tags`, { tag_ids: tagIds });
};

export const createTag = async (payload: { label: string; color?: string | null }): Promise<TagResponse> => {
  const { data } = await api.post<TagResponse>('/tags', payload);
  return data;
};

export const createFolder = async (payload: { name: string; parent_id?: Identifier | null }): Promise<unknown> => {
  const { data } = await api.post('/folders', payload);
  return data;
};

export const assignCorrespondentsBulk = async <T = unknown>(
  payload: Record<string, unknown>,
): Promise<T> => {
  const { data } = await api.post<T>('/documents/bulk/correspondents', payload);
  return data;
};

export const createApiToken = async (payload: {
  capability_set_id: Identifier;
  label?: string;
  expires_at?: string;
}): Promise<{ token_info?: ApiTokenRecord; token?: string }> => {
  const { data } = await api.post('/profile/api-tokens', payload);
  return data as { token_info?: ApiTokenRecord; token?: string };
};

export const regenerateApiToken = async (
  tokenId: Identifier,
): Promise<{ token_info?: ApiTokenRecord; token?: string }> => {
  const { data } = await api.post(`/profile/api-tokens/${tokenId}/regenerate`);
  return data as { token_info?: ApiTokenRecord; token?: string };
};

export const startPasskeyRegistration = async (): Promise<unknown> => {
  const { data } = await api.post('/auth/passkeys/register/start', {});
  return data;
};

export const finishPasskeyRegistration = async (payload: unknown): Promise<unknown> => {
  const { data } = await api.post('/auth/passkeys/register/finish', payload);
  return data;
};

export const startPasskeyLogin = async (username: string): Promise<unknown> => {
  const { data } = await api.post('/auth/passkeys/login/start', { username });
  return data;
};

export const finishPasskeyLogin = async (payload: unknown): Promise<unknown> => {
  const { data } = await api.post('/auth/passkeys/login/finish', payload);
  return data;
};

export const performLogin = async (payload: Record<string, unknown>): Promise<unknown> => {
  const { data } = await api.post('/auth/login', payload);
  return data;
};

export const refreshSession = async (): Promise<{ access_token?: string; tenant?: unknown }> => {
  const { data } = await api.post('/auth/refresh', undefined, { skipAuthRefresh: true } as AuthRequestConfig);
  return data as { access_token?: string; tenant?: unknown };
};

export const logoutSession = async (): Promise<void> => {
  await api.post('/auth/logout');
};

export const selectTenant = async (
  payload: { tenant_id: Identifier },
  selectionToken: string,
): Promise<unknown> => {
  const { data } = await api.post('/auth/select-tenant', payload, {
    headers: {
      Authorization: `Bearer ${selectionToken}`,
    },
  });
  return data;
};

export const startSignup = async (username: string): Promise<unknown> => {
  const { data } = await api.post('/auth/signup/start', { username });
  return data;
};

export const finishSignup = async (payload: unknown): Promise<unknown> => {
  const { data } = await api.post('/auth/signup/finish', payload);
  return data;
};

export const updateDocument = async (
  id: Identifier,
  payload: Record<string, unknown>,
): Promise<DocumentResponse> => {
  const { data } = await api.patch<DocumentResponse>(`/documents/${id}`, payload);
  return data;
};

export const moveDocumentToFolder = async (id: Identifier, folderId: Identifier | null): Promise<void> => {
  await api.patch(`/documents/${id}/folder`, { folder_id: folderId });
};

export const deleteDocumentTag = async (documentId: Identifier, tagId: Identifier): Promise<void> => {
  await api.delete(`/documents/${documentId}/tags/${tagId}`);
};

export const deleteFolder = async (folderId: Identifier): Promise<void> => {
  await api.delete(`/folders/${folderId}`);
};

export const moveFolder = async (folderId: Identifier, parentId: Identifier | null): Promise<void> => {
  await api.patch(`/folders/${folderId}`, { parent_id: parentId });
};

export const renameFolder = async (folderId: Identifier, name: string): Promise<void> => {
  await api.patch(`/folders/${folderId}`, { name });
};

export const createCapabilitySet = async (
  payload: { slug?: string; label?: string; capabilities: string[] },
): Promise<CapabilitySetResponse & { label?: string }> => {
  const { data } = await api.post<CapabilitySetResponse & { label?: string }>('/capability-sets', payload);
  return data;
};

export const updateCapabilitySet = async (
  id: Identifier,
  payload: { slug?: string; label?: string; capabilities?: string[] },
): Promise<CapabilitySetResponse & { label?: string }> => {
  const { data } = await api.patch<CapabilitySetResponse & { label?: string }>(`/capability-sets/${id}`, payload);
  return data;
};

export const deleteCapabilitySet = async (id: Identifier): Promise<void> => {
  await api.delete(`/capability-sets/${id}`);
};

export const deleteApiToken = async (tokenId: Identifier): Promise<void> => {
  await api.delete(`/profile/api-tokens/${tokenId}`);
};

export const deletePasskey = async (
  passkeyId: Identifier,
  options: { reason?: string } = {},
): Promise<void> => {
  const query = options.reason ? `?reason=${encodeURIComponent(options.reason)}` : '';
  await api.delete(`/profile/passkeys/${passkeyId}${query}`);
};

export const listTenants = async (): Promise<TenantSnippet[]> => {
  const { data } = await api.get<{ tenants?: TenantSnippet[] } | TenantSnippet[]>('/tenants');
  if (Array.isArray(data)) {
    return data;
  }
  return Array.isArray(data?.tenants) ? data.tenants : [];
};

export const setAuthToken = (token: string) => {
  api.defaults.headers.common.Authorization = `Bearer ${token}`;
};

export const clearAuthToken = () => {
  delete api.defaults.headers.common.Authorization;
};

export const setAuthRefreshHandlers = (handlers: AuthRefreshHandlers) => {
  authRefreshHandlers = handlers;
};

const performTokenRefresh = async (): Promise<string> => {
  if (refreshPromise) {
    return refreshPromise;
  }

  refreshPromise = refreshSession()
    .then((data) => {
      const token = data?.access_token;
      if (!token) {
        throw new Error('Missing access token in refresh response');
      }
      setAuthToken(token);
      authRefreshHandlers.onRefreshSuccess?.(token, { tenant: data?.tenant });
      return token;
    })
    .catch((error) => {
      authRefreshHandlers.onRefreshFailure?.(error);
      throw error;
    })
    .finally(() => {
      refreshPromise = null;
    });

  return refreshPromise;
};

api.interceptors.response.use(
  (response) => response,
  async (error: AxiosError) => {
    const response = error.response;
    const config = (error.config || {}) as AuthAwareRequestConfig;
    if (!response || response.status !== 401 || config._retry || config.skipAuthRefresh) {
      return Promise.reject(error);
    }

    config._retry = true;

    try {
      const token = await performTokenRefresh();
      const headers = (config.headers ?? {}) as Record<string, unknown>;
      headers.Authorization = `Bearer ${token}`;
      config.headers = headers as AuthAwareRequestConfig['headers'];
      return api(config);
    } catch (refreshError) {
      return Promise.reject(refreshError);
    }
  },
);

export type {
  DownloadLink,
  DocumentResponse,
  AssetResponse,
  FolderTreeNode,
  CapabilitySetResponse,
  CapabilityResponse,
  ApiTokenRecord,
  PasskeySummary,
  Identifier,
  TenantSnippet,
} from './apiTypes';
