/**
 * Experience Platform API client — Torbat Pages (real backend only).
 * Browser → /api/experience BFF; SSR → direct upstream :8008
 */

import { getValidAccessToken, refreshSession } from "@/lib/auth";
import type {
  CapabilitiesResponse,
  HealthResponse,
  ListParams,
  MetricsResponse,
  PaginatedList,
} from "@/modules/experience/types";

const EXPERIENCE_BASE =
  typeof window !== "undefined"
    ? "/api/experience"
    : process.env.EXPERIENCE_SERVICE_URL ||
      process.env.NEXT_PUBLIC_EXPERIENCE_API_URL ||
      "http://localhost:8008";

export class ExperienceApiError extends Error {
  constructor(
    public status: number,
    public code: string,
    message: string
  ) {
    super(message);
    this.name = "ExperienceApiError";
  }
}

/** Never surface raw objects as "[object Object]" in UI toasts/ErrorState. */
export function formatExperienceError(err: unknown, fallback = "خطا در ارتباط با تربت پیجز"): string {
  if (err instanceof ExperienceApiError) return err.message || fallback;
  if (err instanceof Error) return err.message || fallback;
  if (typeof err === "string" && err.trim()) return err;
  return fallback;
}

function stringifyApiDetail(detail: unknown): string | null {
  if (detail == null) return null;
  if (typeof detail === "string") return detail;
  if (Array.isArray(detail)) {
    const parts = detail
      .map((item) => {
        if (typeof item === "string") return item;
        if (item && typeof item === "object") {
          const row = item as { msg?: unknown; loc?: unknown; message?: unknown };
          const where = Array.isArray(row.loc)
            ? row.loc.filter((x) => x !== "body" && x !== "query").join(".")
            : "";
          const msg = row.msg ?? row.message;
          if (typeof msg === "string") return where ? `${where}: ${msg}` : msg;
        }
        return null;
      })
      .filter(Boolean);
    return parts.length ? parts.join(" · ") : null;
  }
  if (typeof detail === "object") {
    const obj = detail as { message?: unknown; code?: unknown; msg?: unknown };
    if (typeof obj.message === "string") return obj.message;
    if (typeof obj.msg === "string") return obj.msg;
    try {
      return JSON.stringify(detail);
    } catch {
      return null;
    }
  }
  return String(detail);
}

type RequestOptions = RequestInit & { tenantId: string; auth?: boolean };

async function request<T>(path: string, options: RequestOptions): Promise<T> {
  const { tenantId, auth = true, headers: customHeaders, ...init } = options;
  const headers = new Headers(customHeaders);
  if (!headers.has("Content-Type") && init.body) {
    headers.set("Content-Type", "application/json");
  }
  headers.set("X-Tenant-ID", tenantId);
  if (auth) {
    const token = await getValidAccessToken();
    if (token) headers.set("Authorization", `Bearer ${token}`);
  }

  const url = path.startsWith("http")
    ? path
    : `${EXPERIENCE_BASE.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}`;

  let res: Response;
  try {
    res = await fetch(url, { ...init, headers });
  } catch (err) {
    const detail = err instanceof Error ? err.message : "network_error";
    throw new Error(`اتصال به سرویس تربت پیجز برقرار نشد (${url}) — ${detail}`);
  }

  if (res.status === 401 && auth) {
    const refreshed = await refreshSession({ force: true });
    if (refreshed) {
      headers.set("Authorization", `Bearer ${refreshed}`);
      res = await fetch(url, { ...init, headers });
    }
  }

  if (!res.ok) {
    const body = await res.json().catch(() => ({}));
    const code =
      (typeof body?.error?.code === "string" && body.error.code) ||
      (typeof body?.code === "string" && body.code) ||
      "unknown_error";
    const message =
      stringifyApiDetail(body?.error?.message) ||
      stringifyApiDetail(body?.error?.details) ||
      stringifyApiDetail(body?.detail) ||
      stringifyApiDetail(body?.message) ||
      res.statusText ||
      `HTTP ${res.status}`;
    throw new ExperienceApiError(res.status, code, message);
  }
  if (res.status === 204) return undefined as T;
  return res.json() as Promise<T>;
}

function qs(params?: Record<string, string | number | boolean | undefined | null>) {
  if (!params) return "";
  const sp = new URLSearchParams();
  Object.entries(params).forEach(([k, v]) => {
    if (v !== undefined && v !== null && v !== "") sp.set(k, String(v));
  });
  const s = sp.toString();
  return s ? `?${s}` : "";
}

export function normalizeList<T = Record<string, unknown>>(
  data: T[] | PaginatedList<T> | { items?: T[] }
): T[] {
  if (Array.isArray(data)) return data;
  if ("items" in data && Array.isArray(data.items)) return data.items;
  return [];
}

function crudResource(basePath: string, softDelete = true) {
  return {
    list: (tenantId: string, params?: ListParams) =>
      request<Record<string, unknown>[] | PaginatedList<Record<string, unknown>>>(
        `${basePath}${qs(params)}`,
        { tenantId }
      ).then(normalizeList),
    get: (tenantId: string, id: string) =>
      request<Record<string, unknown>>(`${basePath}/${id}`, { tenantId }),
    create: (tenantId: string, body: Record<string, unknown>) =>
      request<Record<string, unknown>>(basePath, {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    update: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<Record<string, unknown>>(`${basePath}/${id}`, {
        tenantId,
        method: "PATCH",
        body: JSON.stringify(body),
      }),
    remove: softDelete
      ? (tenantId: string, id: string) =>
          request<Record<string, unknown>>(`${basePath}/${id}/delete`, {
            tenantId,
            method: "POST",
          })
      : (tenantId: string, id: string) =>
          request<void>(`${basePath}/${id}`, { tenantId, method: "DELETE" }),
  };
}

function httpDeleteResource(basePath: string) {
  return {
    list: (tenantId: string, params?: ListParams) =>
      request<Record<string, unknown>[] | PaginatedList<Record<string, unknown>>>(
        `${basePath}${qs(params)}`,
        { tenantId }
      ).then(normalizeList),
    get: (tenantId: string, id: string) =>
      request<Record<string, unknown>>(`${basePath}/${id}`, { tenantId }),
    create: (tenantId: string, body: Record<string, unknown>) =>
      request<Record<string, unknown>>(basePath, {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    update: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<Record<string, unknown>>(`${basePath}/${id}`, {
        tenantId,
        method: "PATCH",
        body: JSON.stringify(body),
      }),
    remove: (tenantId: string, id: string) =>
      request<void>(`${basePath}/${id}`, { tenantId, method: "DELETE" }),
  };
}

export const experienceApi = {
  health: (): Promise<HealthResponse> =>
    fetch(`${EXPERIENCE_BASE}/health`).then((r) => r.json()),

  capabilities: (): Promise<CapabilitiesResponse> =>
    fetch(`${EXPERIENCE_BASE}/capabilities`).then((r) => r.json()),

  metrics: (): Promise<MetricsResponse> =>
    fetch(`${EXPERIENCE_BASE}/metrics`).then((r) => r.json()),

  workspaces: crudResource("/api/v1/workspaces"),
  localeProfiles: crudResource("/api/v1/locale-profiles"),
  externalProviders: crudResource("/api/v1/external-providers"),
  renderEngines: crudResource("/api/v1/render-engines"),
  configurations: crudResource("/api/v1/configurations"),

  settings: {
    list: (tenantId: string, params?: ListParams) =>
      request<Record<string, unknown>[]>(`/api/v1/settings${qs(params)}`, { tenantId }).then(
        normalizeList
      ),
    upsert: (tenantId: string, body: Record<string, unknown>) =>
      request<Record<string, unknown>>(`/api/v1/settings`, {
        tenantId,
        method: "PUT",
        body: JSON.stringify(body),
      }),
  },

  audit: {
    list: (tenantId: string, params?: ListParams) =>
      request<Record<string, unknown>[]>(`/api/v1/audit${qs(params)}`, { tenantId }).then(
        normalizeList
      ),
  },

  sites: crudResource("/api/v1/sites"),
  pages: crudResource("/api/v1/pages"),
  siteDomains: crudResource("/api/v1/site-domains"),

  components: crudResource("/api/v1/components"),
  componentVersions: {
    ...crudResource("/api/v1/component-versions", false),
    publish: (tenantId: string, id: string, body: Record<string, unknown> = {}) =>
      request<Record<string, unknown>>(`/api/v1/component-versions/${id}/publish`, {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
  },
  pageComponentPlacements: crudResource("/api/v1/page-component-placements"),

  themes: crudResource("/api/v1/themes"),
  themeVersions: crudResource("/api/v1/theme-versions", false),
  layouts: crudResource("/api/v1/layouts"),
  layoutVersions: crudResource("/api/v1/layout-versions", false),
  siteThemeBindings: {
    list: (tenantId: string, params?: ListParams) =>
      request<Record<string, unknown>[]>(`/api/v1/site-theme-bindings${qs(params)}`, {
        tenantId,
      }).then(normalizeList),
    create: (tenantId: string, body: Record<string, unknown>) =>
      request<Record<string, unknown>>(`/api/v1/site-theme-bindings`, {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    unassign: (tenantId: string, id: string) =>
      request<Record<string, unknown>>(`/api/v1/site-theme-bindings/${id}/unassign`, {
        tenantId,
        method: "POST",
      }),
  },
  pageLayoutBindings: {
    list: (tenantId: string, params?: ListParams) =>
      request<Record<string, unknown>[]>(`/api/v1/page-layout-bindings${qs(params)}`, {
        tenantId,
      }).then(normalizeList),
    create: (tenantId: string, body: Record<string, unknown>) =>
      request<Record<string, unknown>>(`/api/v1/page-layout-bindings`, {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    unassign: (tenantId: string, id: string) =>
      request<Record<string, unknown>>(`/api/v1/page-layout-bindings/${id}/unassign`, {
        tenantId,
        method: "POST",
      }),
  },

  templates: crudResource("/api/v1/templates"),
  templateVersions: crudResource("/api/v1/template-versions", false),
  templateInstantiations: {
    list: (tenantId: string, params?: ListParams) =>
      request<Record<string, unknown>[]>(`/api/v1/template-instantiations${qs(params)}`, {
        tenantId,
      }).then(normalizeList),
    get: (tenantId: string, id: string) =>
      request<Record<string, unknown>>(`/api/v1/template-instantiations/${id}`, { tenantId }),
    create: (tenantId: string, body: Record<string, unknown>) =>
      request<Record<string, unknown>>(`/api/v1/template-instantiations`, {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
  },

  localizations: httpDeleteResource("/api/v1/localizations"),
  mediaRefs: httpDeleteResource("/api/v1/media-refs"),

  forms: httpDeleteResource("/api/v1/forms"),
  formSubmissions: {
    list: (tenantId: string, params?: ListParams) =>
      request<Record<string, unknown>[]>(`/api/v1/form-submissions${qs(params)}`, {
        tenantId,
      }).then(normalizeList),
    get: (tenantId: string, id: string) =>
      request<Record<string, unknown>>(`/api/v1/form-submissions/${id}`, { tenantId }),
    create: (tenantId: string, body: Record<string, unknown>) =>
      request<Record<string, unknown>>(`/api/v1/form-submissions`, {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    update: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<Record<string, unknown>>(`/api/v1/form-submissions/${id}`, {
        tenantId,
        method: "PATCH",
        body: JSON.stringify(body),
      }),
  },
  surveys: httpDeleteResource("/api/v1/surveys"),
  surveyResponses: {
    list: (tenantId: string, params?: ListParams) =>
      request<Record<string, unknown>[]>(`/api/v1/survey-responses${qs(params)}`, {
        tenantId,
      }).then(normalizeList),
    get: (tenantId: string, id: string) =>
      request<Record<string, unknown>>(`/api/v1/survey-responses/${id}`, { tenantId }),
    update: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<Record<string, unknown>>(`/api/v1/survey-responses/${id}`, {
        tenantId,
        method: "PATCH",
        body: JSON.stringify(body),
      }),
  },

  publishReleases: {
    list: (tenantId: string, params?: ListParams) =>
      request<Record<string, unknown>[]>(`/api/v1/publish-releases${qs(params)}`, {
        tenantId,
      }).then(normalizeList),
    get: (tenantId: string, id: string) =>
      request<Record<string, unknown>>(`/api/v1/publish-releases/${id}`, { tenantId }),
    create: (tenantId: string, body: Record<string, unknown>) =>
      request<Record<string, unknown>>(`/api/v1/publish-releases`, {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    update: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<Record<string, unknown>>(`/api/v1/publish-releases/${id}`, {
        tenantId,
        method: "PATCH",
        body: JSON.stringify(body),
      }),
  },
  domainEdgeBindings: {
    list: (tenantId: string, params?: ListParams) =>
      request<Record<string, unknown>[]>(`/api/v1/domain-edge-bindings${qs(params)}`, {
        tenantId,
      }).then(normalizeList),
    get: (tenantId: string, id: string) =>
      request<Record<string, unknown>>(`/api/v1/domain-edge-bindings/${id}`, { tenantId }),
    create: (tenantId: string, body: Record<string, unknown>) =>
      request<Record<string, unknown>>(`/api/v1/domain-edge-bindings`, {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    update: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<Record<string, unknown>>(`/api/v1/domain-edge-bindings/${id}`, {
        tenantId,
        method: "PATCH",
        body: JSON.stringify(body),
      }),
  },
  seoMetadata: {
    list: (tenantId: string, params?: ListParams) =>
      request<Record<string, unknown>[]>(`/api/v1/seo-metadata${qs(params)}`, { tenantId }).then(
        normalizeList
      ),
    get: (tenantId: string, id: string) =>
      request<Record<string, unknown>>(`/api/v1/seo-metadata/${id}`, { tenantId }),
    create: (tenantId: string, body: Record<string, unknown>) =>
      request<Record<string, unknown>>(`/api/v1/seo-metadata`, {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    update: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<Record<string, unknown>>(`/api/v1/seo-metadata/${id}`, {
        tenantId,
        method: "PATCH",
        body: JSON.stringify(body),
      }),
  },
  sitemapShells: {
    list: (tenantId: string, params?: ListParams) =>
      request<Record<string, unknown>[]>(`/api/v1/sitemap-shells${qs(params)}`, { tenantId }).then(
        normalizeList
      ),
    get: (tenantId: string, id: string) =>
      request<Record<string, unknown>>(`/api/v1/sitemap-shells/${id}`, { tenantId }),
    create: (tenantId: string, body: Record<string, unknown>) =>
      request<Record<string, unknown>>(`/api/v1/sitemap-shells`, {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    update: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<Record<string, unknown>>(`/api/v1/sitemap-shells/${id}`, {
        tenantId,
        method: "PATCH",
        body: JSON.stringify(body),
      }),
  },
  robotsShells: {
    list: (tenantId: string, params?: ListParams) =>
      request<Record<string, unknown>[]>(`/api/v1/robots-shells${qs(params)}`, { tenantId }).then(
        normalizeList
      ),
    get: (tenantId: string, id: string) =>
      request<Record<string, unknown>>(`/api/v1/robots-shells/${id}`, { tenantId }),
    create: (tenantId: string, body: Record<string, unknown>) =>
      request<Record<string, unknown>>(`/api/v1/robots-shells`, {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    update: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<Record<string, unknown>>(`/api/v1/robots-shells/${id}`, {
        tenantId,
        method: "PATCH",
        body: JSON.stringify(body),
      }),
  },
  pwaManifests: {
    list: (tenantId: string, params?: ListParams) =>
      request<Record<string, unknown>[]>(`/api/v1/pwa-manifests${qs(params)}`, { tenantId }).then(
        normalizeList
      ),
    get: (tenantId: string, id: string) =>
      request<Record<string, unknown>>(`/api/v1/pwa-manifests/${id}`, { tenantId }),
    create: (tenantId: string, body: Record<string, unknown>) =>
      request<Record<string, unknown>>(`/api/v1/pwa-manifests`, {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    update: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<Record<string, unknown>>(`/api/v1/pwa-manifests/${id}`, {
        tenantId,
        method: "PATCH",
        body: JSON.stringify(body),
      }),
  },

  analyticsReportDefinitions: httpDeleteResource("/api/v1/analytics-report-definitions"),
  analyticsSnapshots: {
    list: (tenantId: string, params?: ListParams) =>
      request<Record<string, unknown>[]>(`/api/v1/analytics-snapshots${qs(params)}`, {
        tenantId,
      }).then(normalizeList),
    get: (tenantId: string, id: string) =>
      request<Record<string, unknown>>(`/api/v1/analytics-snapshots/${id}`, { tenantId }),
    refresh: (tenantId: string, body: Record<string, unknown> = {}) =>
      request<Record<string, unknown>>(`/api/v1/analytics-snapshots/refresh`, {
        tenantId,
        method: "POST",
        body: JSON.stringify(body),
      }),
    update: (tenantId: string, id: string, body: Record<string, unknown>) =>
      request<Record<string, unknown>>(`/api/v1/analytics-snapshots/${id}`, {
        tenantId,
        method: "PATCH",
        body: JSON.stringify(body),
      }),
    remove: (tenantId: string, id: string) =>
      request<void>(`/api/v1/analytics-snapshots/${id}`, { tenantId, method: "DELETE" }),
  },

  widgetDefinitions: httpDeleteResource("/api/v1/widget-definitions"),
  widgetInstances: httpDeleteResource("/api/v1/widget-instances"),
};
