import { axiosInstance } from '../../axiosApi';
import { oauthStartUrl } from '../auth/sso';

export type DriveProvider = 'google' | 'microsoft';

export type DriveConnectionKind = 'personal' | 'company';

/** intent query param sent to /auth/oauth/:provider/start/ for drive-linking flows (#83/#84). */
export type DriveConnectIntent = 'link_drive' | 'link_company_drive';

export type DriveSyncStatus = 'ok' | 'error' | 'pending' | 'never';

export type DriveConnectionType = {
  id: number;
  provider: DriveProvider;
  kind?: DriveConnectionKind;
  is_active?: boolean;
  external_account_email?: string | null;
  selected_resource_ids?: string[];
  selected_resource_labels?: string[];
  last_sync_at?: string | null;
  last_sync_status?: DriveSyncStatus | string | null;
  last_sync_error?: string;
  sync_total?: number;
  sync_processed?: number;
  sync_added?: number;
  sync_updated?: number;
  sync_failed?: number;
  created?: string;
};

export type DriveSyncEnqueueResponse = {
  queued?: boolean;
  connection: DriveConnectionType;
};

export async function fetchDriveConnections(): Promise<DriveConnectionType[]> {
  const { data } = await axiosInstance.get<DriveConnectionType[]>('/drive/connections/');
  return Array.isArray(data) ? data : [];
}

export async function disconnectDriveConnection(connectionId: number): Promise<void> {
  await axiosInstance.delete(`/drive/connections/${connectionId}/`);
}

export type DriveResourceSelection = {
  resource_ids: string[];
  resource_labels: string[];
};

export async function saveDriveResourceSelection(
  connectionId: number,
  selection: DriveResourceSelection
): Promise<DriveConnectionType> {
  const { data } = await axiosInstance.post<DriveConnectionType>(
    `/drive/connections/${connectionId}/resources/`,
    selection
  );
  return data;
}

/** Enqueue a Drive sync (#57/#90). Returns quickly with pending status. */
export async function syncDriveConnection(
  connectionId: number
): Promise<DriveSyncEnqueueResponse> {
  const { data } = await axiosInstance.post<DriveSyncEnqueueResponse>(
    `/drive/connections/${connectionId}/sync/`
  );
  if (!data?.connection) {
    throw new Error('Drive sync did not return a connection.');
  }
  return data;
}

const DEFAULT_SYNC_POLL_MS = 1500;
const DEFAULT_SYNC_TIMEOUT_MS = 120_000;

/** Poll connections until the target leaves ``pending`` (or timeout). */
export async function waitForDriveSyncSettlement(
  connectionId: number,
  options?: {
    intervalMs?: number;
    timeoutMs?: number;
    onProgress?: (connection: DriveConnectionType) => void;
  }
): Promise<DriveConnectionType> {
  const intervalMs = options?.intervalMs ?? DEFAULT_SYNC_POLL_MS;
  const timeoutMs = options?.timeoutMs ?? DEFAULT_SYNC_TIMEOUT_MS;
  const deadline = Date.now() + timeoutMs;

  while (Date.now() < deadline) {
    const connections = await fetchDriveConnections();
    const connection = connections.find((item) => item.id === connectionId);
    if (!connection) {
      throw new Error('Drive connection disappeared while syncing.');
    }
    options?.onProgress?.(connection);
    if (connection.last_sync_status !== 'pending') {
      return connection;
    }
    await new Promise((resolve) => setTimeout(resolve, intervalMs));
  }
  throw new Error('Drive sync timed out. Check status and try again.');
}

/** Percent complete when total is known; otherwise null (indeterminate). */
export function driveSyncProgressPercent(connection: DriveConnectionType): number | null {
  const total = connection.sync_total ?? 0;
  if (total <= 0) {
    return null;
  }
  const processed = Math.min(connection.sync_processed ?? 0, total);
  return Math.round((processed / total) * 100);
}

/** Shorten long provider JSON error blobs for toasts. */
export function formatDriveSyncError(raw?: string | null, maxLen = 280): string {
  const text = (raw || '').trim();
  if (!text) {
    return 'Drive sync failed. Try again or check provider API settings.';
  }
  if (text.length <= maxLen) {
    return text;
  }
  return `${text.slice(0, maxLen).trim()}…`;
}

/** Absolute backend OAuth start URL for a drive-link flow (personal or company). */
export function driveConnectUrl(provider: DriveProvider, intent: DriveConnectIntent): string {
  return oauthStartUrl(provider, intent);
}

/**
 * Begin Drive link: call start with Bearer JWT, then navigate to IdP authorize URL.
 * Full-page assign alone cannot send Authorization, so the backend returns JSON.
 */
export async function connectDrive(
  provider: DriveProvider,
  intent: DriveConnectIntent
): Promise<void> {
  const { data } = await axiosInstance.get<{ authorize_url: string }>(
    `/auth/oauth/${provider}/start/`,
    {
      params: { intent, response: 'json' },
      headers: { Accept: 'application/json' },
    }
  );
  if (!data?.authorize_url) {
    throw new Error('Drive connect did not return an authorize URL.');
  }
  window.location.assign(data.authorize_url);
}

/** Splits a comma/newline separated textarea/input value into trimmed, non-empty resource ids. */
export function parseResourceIdsInput(raw: string): string[] {
  return raw
    .split(/[,\n]/)
    .map((value) => value.trim())
    .filter(Boolean);
}
