// ═══════════════════════════════════════════════════════════
// Factory API HTTP Client
// Talks to the Factory REST API at http://localhost:4000/v1
// ═══════════════════════════════════════════════════════════

import type {
  ApiResponse,
  ApiError,
  Pipeline,
  Task,
  TaskStats,
  Approval,
  Agent,
  Asset,
  AuditEntry,
  DashboardSummary,
  PipelineStageRecord,
  PipelineDetailState,
  ServerStatus,
  TestResults,
  BlockerInfo,
  PipelineTemplateConfig,
  PipelineStatus,
  PipelineStage,
  Priority,
  TaskStatus,
  TaskType,
  TaskDecision,
} from "./types.js";

const API_BASE = process.env.FACTORY_API_URL || "http://localhost:4000/v1";
const API_KEY = process.env.FACTORY_API_KEY || "";

class FactoryApiError extends Error {
  constructor(
    public code: string,
    message: string,
    public statusCode: number,
    public details?: Record<string, unknown>,
  ) {
    super(message);
    this.name = "FactoryApiError";
  }
}

async function request<T>(
  method: string,
  path: string,
  body?: unknown,
  params?: Record<string, string | number | boolean | undefined>,
): Promise<T> {
  const url = new URL(`${API_BASE}${path}`);
  if (params) {
    for (const [key, value] of Object.entries(params)) {
      if (value !== undefined) {
        url.searchParams.set(key, String(value));
      }
    }
  }

  const headers: Record<string, string> = {
    "Content-Type": "application/json",
    "X-Request-Id": crypto.randomUUID(),
  };

  if (API_KEY) {
    headers["X-API-Key"] = API_KEY;
  }

  const response = await fetch(url.toString(), {
    method,
    headers,
    body: body ? JSON.stringify(body) : undefined,
  });

  if (!response.ok) {
    let errorBody: ApiError | undefined;
    try {
      errorBody = await response.json() as ApiError;
    } catch {
      // response was not JSON
    }

    throw new FactoryApiError(
      errorBody?.error?.code || "UNKNOWN_ERROR",
      errorBody?.error?.message || `HTTP ${response.status}: ${response.statusText}`,
      response.status,
      errorBody?.error?.details,
    );
  }

  // 204 No Content
  if (response.status === 204) {
    return undefined as T;
  }

  const json = (await response.json()) as ApiResponse<T>;
  return json.data;
}

// ═══════════════════════════════════════
// Pipeline Endpoints
// ═══════════════════════════════════════

export async function listPipelines(params?: {
  status?: PipelineStatus | "all";
  stage?: PipelineStage;
  priority?: Priority;
  assigneeId?: string;
  search?: string;
  page?: number;
  limit?: number;
  sort?: string;
}): Promise<Pipeline[]> {
  return request<Pipeline[]>("GET", "/pipelines", undefined, params as Record<string, string | number | boolean | undefined>);
}

export async function getPipeline(pipelineId: string): Promise<Pipeline> {
  return request<Pipeline>("GET", `/pipelines/${pipelineId}`);
}

export async function createPipeline(body: {
  name: string;
  platform: string;
  template?: string;
  priority?: Priority;
  config?: Record<string, unknown>;
}): Promise<Pipeline> {
  return request<Pipeline>("POST", "/pipelines", body);
}

export async function updatePipeline(
  pipelineId: string,
  body: {
    priority?: Priority;
    status?: PipelineStatus;
    config?: Record<string, unknown>;
    assigneeId?: string | null;
    metadata?: Record<string, unknown>;
  },
): Promise<Pipeline> {
  return request<Pipeline>("PATCH", `/pipelines/${pipelineId}`, body);
}

export async function getPipelineStages(pipelineId: string): Promise<PipelineStageRecord[]> {
  return request<PipelineStageRecord[]>("GET", `/pipelines/${pipelineId}/stages`);
}

export async function advanceStage(
  pipelineId: string,
  body: {
    targetStage?: PipelineStage;
    skipValidation?: boolean;
    notes?: string;
  },
): Promise<{ stage: PipelineStageRecord; tasksCreated: Task[] }> {
  return request("POST", `/pipelines/${pipelineId}/stages/advance`, body);
}

// ═══════════════════════════════════════
// Task Endpoints
// ═══════════════════════════════════════

export async function listTasks(params?: {
  status?: TaskStatus;
  priority?: Priority;
  type?: TaskType;
  pipelineId?: string;
  assigneeId?: string;
  slaBreached?: boolean;
  page?: number;
  limit?: number;
  sort?: string;
}): Promise<Task[]> {
  return request<Task[]>("GET", "/tasks", undefined, params as Record<string, string | number | boolean | undefined>);
}

export async function getTask(taskId: string): Promise<Task> {
  return request<Task>("GET", `/tasks/${taskId}`);
}

export async function completeTask(
  taskId: string,
  body: {
    decision: TaskDecision;
    notes?: string;
    decisionData?: Record<string, unknown>;
  },
): Promise<Task> {
  return request<Task>("POST", `/tasks/${taskId}/complete`, body);
}

export async function getTaskStats(): Promise<TaskStats> {
  return request<TaskStats>("GET", "/tasks/stats");
}

// ═══════════════════════════════════════
// Approval Endpoints
// ═══════════════════════════════════════

export async function approveApproval(
  approvalId: string,
  body: { notes?: string; conditions?: string[] },
): Promise<Approval> {
  return request<Approval>("POST", `/approvals/${approvalId}/approve`, body);
}

export async function rejectApproval(
  approvalId: string,
  body: { reason: string; requestedChanges?: string[] },
): Promise<Approval> {
  return request<Approval>("POST", `/approvals/${approvalId}/reject`, body);
}

// ═══════════════════════════════════════
// Agent Endpoints
// ═══════════════════════════════════════

export async function listAgents(): Promise<Agent[]> {
  return request<Agent[]>("GET", "/agents");
}

export async function getAgent(agentId: string): Promise<Agent> {
  return request<Agent>("GET", `/agents/${agentId}`);
}

// ═══════════════════════════════════════
// Asset Endpoints
// ═══════════════════════════════════════

export async function listAssets(
  pipelineId: string,
  params?: { type?: string; stage?: string },
): Promise<Asset[]> {
  return request<Asset[]>(
    "GET",
    `/pipelines/${pipelineId}/assets`,
    undefined,
    params as Record<string, string | number | boolean | undefined>,
  );
}

// ═══════════════════════════════════════
// Dashboard Endpoints
// ═══════════════════════════════════════

export async function getDashboardSummary(): Promise<DashboardSummary> {
  return request<DashboardSummary>("GET", "/dashboard/summary");
}

// ═══════════════════════════════════════
// Audit Endpoints
// ═══════════════════════════════════════

export async function getAuditLog(params?: {
  entityType?: string;
  entityId?: string;
  actorId?: string;
  action?: string;
  since?: string;
  until?: string;
  page?: number;
  limit?: number;
}): Promise<AuditEntry[]> {
  return request<AuditEntry[]>("GET", "/audit", undefined, params as Record<string, string | number | boolean | undefined>);
}

// ═══════════════════════════════════════
// Search (composite)
// ═══════════════════════════════════════

export interface SearchResults {
  pipelines: Pipeline[];
  tasks: Task[];
  assets: Asset[];
  auditEntries: AuditEntry[];
}

export async function search(
  query: string,
  entityTypes?: string[],
  limit?: number,
): Promise<SearchResults> {
  const results: SearchResults = {
    pipelines: [],
    tasks: [],
    assets: [],
    auditEntries: [],
  };

  const types = entityTypes || ["pipeline", "task", "asset", "audit"];
  const perTypeLimit = Math.ceil((limit || 10) / types.length);

  const promises: Promise<void>[] = [];

  if (types.includes("pipeline")) {
    promises.push(
      listPipelines({ search: query, limit: perTypeLimit })
        .then((p) => { results.pipelines = p; })
        .catch(() => {}),
    );
  }
  if (types.includes("task")) {
    promises.push(
      listTasks({ limit: perTypeLimit })
        .then((t) => {
          results.tasks = t.filter(
            (task) =>
              task.title.toLowerCase().includes(query.toLowerCase()) ||
              task.description?.toLowerCase().includes(query.toLowerCase()),
          );
        })
        .catch(() => {}),
    );
  }
  if (types.includes("audit")) {
    promises.push(
      getAuditLog({ limit: perTypeLimit })
        .then((a) => {
          results.auditEntries = a.filter(
            (entry) => entry.action.toLowerCase().includes(query.toLowerCase()),
          );
        })
        .catch(() => {}),
    );
  }

  await Promise.all(promises);
  return results;
}

// ═══════════════════════════════════════
// Deploy & Test triggers
// ═══════════════════════════════════════

export interface DeployResult {
  deploymentId: string;
  pipelineId: string;
  target: "staging" | "production";
  status: "started" | "succeeded" | "failed";
  version: string;
  url?: string;
  error?: string;
}

export async function triggerDeploy(body: {
  pipeline_id: string;
  target: "staging" | "production";
  version?: string;
  dry_run?: boolean;
}): Promise<DeployResult> {
  return request<DeployResult>("POST", `/pipelines/${body.pipeline_id}/deploy`, body);
}

export interface TestRunResult {
  runId: string;
  pipelineId: string;
  testType: string;
  environment: string;
  status: "queued" | "running" | "completed" | "failed";
  results?: TestResults;
}

export async function triggerTests(body: {
  pipeline_id: string;
  test_type?: string;
  environment?: string;
}): Promise<TestRunResult> {
  return request<TestRunResult>("POST", `/pipelines/${body.pipeline_id}/tests/run`, body);
}

// ═══════════════════════════════════════
// Blockers (composite query)
// ═══════════════════════════════════════

export async function getBlockers(pipelineId?: string): Promise<BlockerInfo[]> {
  const params: Record<string, string | undefined> = {};
  if (pipelineId) params.pipeline_id = pipelineId;
  return request<BlockerInfo[]>("GET", "/tasks/blockers", undefined, params);
}

// ═══════════════════════════════════════
// Feedback
// ═══════════════════════════════════════

export async function submitFeedback(event: unknown): Promise<{ id: string }> {
  return request<{ id: string }>("POST", "/feedback", event);
}

// Export the error class for handling
export { FactoryApiError };
