/**
 * HttpBrainAdapter — BrainPort implementation backed by the Brain REST API.
 *
 * Provides concrete implementations of `archivePostmortem` and `archiveLesson`
 * that delegate to `archiveKnowledge` with the reserved category + tag
 * conventions defined in the campaign orchestrator design (Phase 9).
 *
 *   - postmortem: category `vauban_postmortem`, tags `["postmortem", "campaign:<slug>", "outcome:<outcome>"]`
 *   - lesson:     category `vauban_lesson`,     tags `["lesson", ...applies_to]`
 *
 * Auth: OAuth 2.1 bearer token (BRAIN_TOKEN env var) or x-api-key
 * (BRAIN_API_KEY env var). At least one must be present.
 */

import type {
  BrainPort,
  BrainEntry,
  BrainEntryInput,
  BrainQueryFilters,
  PostmortemInput,
  LessonInput,
} from "../ports/brain.js";

export interface HttpBrainAdapterOptions {
  /** Brain REST API base URL (e.g. https://brain.api.vauban.tech). */
  baseUrl: string;
  /** Brain instance UUID. */
  brainId: string;
  /** OAuth 2.1 bearer token. Takes precedence over apiKey. */
  token?: string;
  /** x-api-key fallback (legacy). */
  apiKey?: string;
  /** Request timeout in milliseconds (default: 10 000). */
  timeoutMs?: number;
}

interface BrainApiSingleResponse {
  success: boolean;
  data: {
    id: string;
    content: string;
    category?: string;
    tags?: string[];
    metadata?: Record<string, unknown>;
    created_at?: string;
  };
}

interface BrainApiListResponse {
  success: boolean;
  data: Array<{
    id: string;
    content: string;
    category?: string;
    tags?: string[];
    metadata?: Record<string, unknown>;
    created_at?: string;
  }>;
}

/**
 * Create an HTTP-backed BrainPort.
 *
 * Includes concrete `archivePostmortem` and `archiveLesson` helpers that
 * delegate to `archiveKnowledge` with reserved categories / tag conventions.
 */
export function createHttpBrainAdapter(
  opts: HttpBrainAdapterOptions
): BrainPort {
  const { baseUrl, brainId, token, apiKey, timeoutMs = 10_000 } = opts;

  const authHeaders: Record<string, string> = token
    ? { Authorization: `Bearer ${token}` }
    : apiKey
      ? { "x-api-key": apiKey }
      : {};

  async function request<T>(path: string, body?: unknown): Promise<T | null> {
    const controller = new AbortController();
    const timer = setTimeout(() => controller.abort(), timeoutMs);
    try {
      const res = await fetch(`${baseUrl}${path}`, {
        method: body !== undefined ? "POST" : "GET",
        headers: { "Content-Type": "application/json", ...authHeaders },
        body: body !== undefined ? JSON.stringify(body) : undefined,
        signal: controller.signal,
      });
      if (!res.ok) {
        console.warn(
          `[brain-http] ${res.status} on ${path}: ${await res.text().catch(() => "")}`
        );
        return null;
      }
      return (await res.json()) as T;
    } catch (err) {
      console.warn(
        `[brain-http] fetch error on ${path}:`,
        (err as Error).message
      );
      return null;
    } finally {
      clearTimeout(timer);
    }
  }

  function mapEntry(raw: BrainApiSingleResponse["data"]): BrainEntry {
    return {
      id: raw.id,
      content: raw.content,
      category: raw.category,
      tags: raw.tags,
      metadata: raw.metadata,
      created_at: raw.created_at,
    };
  }

  const adapter: BrainPort = {
    async archiveKnowledge(entry: BrainEntryInput): Promise<BrainEntry | null> {
      const result = await request<BrainApiSingleResponse>("/api/knowledge", {
        ...entry,
        brain_id: brainId,
      });
      if (!result?.success) return null;
      return mapEntry(result.data);
    },

    async queryKnowledge(
      query: string,
      filters?: BrainQueryFilters
    ): Promise<BrainEntry[]> {
      const params = new URLSearchParams();
      params.set("q", query);
      params.set("brain_id", brainId);
      // Brain API accepts only a single `tags` string param.
      // Pass the first as server-side filter; remaining applied in-memory.
      const extraTags = filters?.tags?.slice(1) ?? [];
      if (filters?.tags?.length) params.set("tags", filters.tags[0]);
      if (filters?.limit)
        params.set("limit", String(Math.min(filters.limit * 3, 100)));

      const result = await request<BrainApiListResponse>(
        `/api/knowledge?${params.toString()}`
      );
      if (!result?.success) return [];
      return result.data
        .filter(
          (e) =>
            (!filters?.category || e.category === filters.category) &&
            extraTags.every((t) => e.tags?.includes(t))
        )
        .map(mapEntry);
    },

    async archivePostmortem(input: PostmortemInput): Promise<void> {
      await adapter.archiveKnowledge({
        content: JSON.stringify(input, null, 2),
        category: "vauban_postmortem",
        tags: [
          "postmortem",
          `campaign:${input.campaign_slug}`,
          `outcome:${input.outcome}`,
        ],
      });
    },

    async archiveLesson(input: LessonInput): Promise<void> {
      await adapter.archiveKnowledge({
        content: JSON.stringify(input, null, 2),
        category: "vauban_lesson",
        tags: ["lesson", ...input.applies_to],
      });
    },
  };

  return adapter;
}
