import { z } from "zod";
import type { Connector, ConnectorContext, ConnectorHealth, ImportResult } from "./types.js";

/**
 * Livsmedelsverkets livsmedelsdatabas – öppna data (laglig kärnkälla för
 * näringsvärden, spec §15/§26/§61.11). Detta är den avsedda produktionskällan
 * som ersätter seed-estimaten i canonical_ingredients.
 *
 * API-dokumentation: https://dataportal.livsmedelsverket.se
 * (endpoint konfigureras via LIVSMEDELSVERKET_API_URL – verifiera aktuell
 * version innan produktionsimport; formatet nedan valideras med Zod).
 */

const livsmedelSchema = z.object({
  nummer: z.number().or(z.string()),
  namn: z.string(),
  naringsvarden: z
    .array(
      z.object({
        namn: z.string(),
        varde: z.number().nullable(),
        enhet: z.string().nullable(),
      }),
    )
    .optional(),
});

export interface LivsmedelsverketItem {
  externalId: string;
  nameSv: string;
  nutrients: Array<{ name: string; value: number | null; unit: string | null }>;
}

export class LivsmedelsverketConnector implements Connector<LivsmedelsverketItem> {
  readonly id = "livsmedelsverket" as const;
  readonly nameSv = "Livsmedelsverkets livsmedelsdatabas";
  readonly legalBasis = "open_data" as const;
  readonly attributionSv = "Näringsdata: Livsmedelsverkets livsmedelsdatabas";

  constructor(
    private readonly baseUrl: string = process.env.LIVSMEDELSVERKET_API_URL ??
      "https://dataportal.livsmedelsverket.se/livsmedel/api/v1",
    private readonly fetchImpl: typeof fetch = fetch,
  ) {}

  async connect(): Promise<void> {
    /* Öppna data – ingen anslutning krävs. */
  }
  async authenticate(): Promise<boolean> {
    return true; // Öppet API utan nyckel.
  }

  async importData(
    _ctx: ConnectorContext,
    params: { offset?: number; limit?: number } = {},
  ): Promise<ImportResult<LivsmedelsverketItem>> {
    const limit = params.limit ?? 100;
    const offset = params.offset ?? 0;
    const warnings: string[] = [];
    const url = `${this.baseUrl}/livsmedel?offset=${offset}&limit=${limit}`;
    const res = await this.fetchImpl(url, { signal: AbortSignal.timeout(20_000) });
    if (!res.ok) {
      throw new Error(`Livsmedelsverket svarade ${res.status} – kontrollera API-version/URL.`);
    }
    const json: unknown = await res.json();
    const rawItems: unknown[] = Array.isArray(json)
      ? json
      : typeof json === "object" &&
          json !== null &&
          Array.isArray((json as { livsmedel?: unknown[] }).livsmedel)
        ? (json as { livsmedel: unknown[] }).livsmedel
        : [];

    const items: LivsmedelsverketItem[] = [];
    for (const raw of rawItems) {
      const parsed = livsmedelSchema.safeParse(raw);
      if (!parsed.success) {
        warnings.push("Rad kunde inte tolkas och hoppades över.");
        continue;
      }
      items.push({
        externalId: String(parsed.data.nummer),
        nameSv: parsed.data.namn,
        nutrients: (parsed.data.naringsvarden ?? []).map((n) => ({
          name: n.namn,
          value: n.varde,
          unit: n.enhet,
        })),
      });
    }
    return { items, source: this.id, importedAt: new Date().toISOString(), warnings };
  }

  async sync(ctx: ConnectorContext): Promise<ImportResult<LivsmedelsverketItem>> {
    return this.importData(ctx);
  }
  async disconnect(): Promise<void> {
    /* noop */
  }

  async healthCheck(): Promise<ConnectorHealth> {
    try {
      const res = await this.fetchImpl(`${this.baseUrl}/livsmedel?offset=0&limit=1`, {
        signal: AbortSignal.timeout(8_000),
      });
      return { ok: res.ok, detail: `status ${res.status}`, checkedAt: new Date().toISOString() };
    } catch (err) {
      return {
        ok: false,
        detail: err instanceof Error ? err.message : String(err),
        checkedAt: new Date().toISOString(),
      };
    }
  }
}
