import type { Principal, SignInCredentials } from '@platform-modules/auth';
import type {
  ContentEntry,
  ContentInput,
  ContentVisibility,
  EntityRef,
  ListQuery,
} from '@platform-modules/content';
import type { AuthClient } from '@platform-modules/auth-react';
import type { ContentClient } from '@platform-modules/content-react';
import type { EntityTranslationStatus } from '@platform-modules/i18n-translator';

export type AdminContentEntry = ContentEntry & {
  translationStatusByLocale?: Record<string, EntityTranslationStatus>;
};

export type ModCmsContentClient = ContentClient & {
  list(query?: ListQuery): Promise<AdminContentEntry[]>;
  translate(id: string): Promise<{ ok: boolean }>;
};

type ApiErrorBody = { error?: { code?: string; message?: string; field?: string } };

export class AdminApiError extends Error {
  override readonly name = 'AdminApiError';

  constructor(
    readonly status: number,
    readonly code: string,
    message: string,
    readonly field?: string,
  ) {
    super(message);
  }
}

export function isAdminApiError(value: unknown): value is AdminApiError {
  return typeof value === 'object'
    && value !== null
    && (value as { name?: unknown }).name === 'AdminApiError'
    && Number.isInteger((value as { status?: unknown }).status)
    && typeof (value as { code?: unknown }).code === 'string'
    && typeof (value as { message?: unknown }).message === 'string'
    && ((value as { field?: unknown }).field === undefined
      || typeof (value as { field?: unknown }).field === 'string');
}

async function apiFetch<T>(url: string, init?: RequestInit): Promise<T> {
  const headers = new Headers(init?.headers);
  if (init?.body != null && !headers.has('Content-Type')) {
    headers.set('Content-Type', 'application/json');
  }

  const res = await fetch(url, {
    ...init,
    credentials: 'same-origin',
    headers,
  });

  const body: unknown = await res.json().catch(() => null);

  if (!res.ok) {
    const err = body as ApiErrorBody | null;
    throw new AdminApiError(
      res.status,
      err?.error?.code ?? `http_${res.status}`,
      err?.error?.message ?? res.statusText,
      err?.error?.field,
    );
  }

  return body as T;
}

function listQueryString(query?: ListQuery): string {
  if (!query) return '';
  const params = new URLSearchParams();
  if (query.status) params.set('status', query.status);
  if (query.type) params.set('type', query.type);
  if (query.limit != null) params.set('limit', String(query.limit));
  if (query.offset != null) params.set('offset', String(query.offset));
  const qs = params.toString();
  return qs ? `?${qs}` : '';
}

export function makeAuthClient(): AuthClient {
  return {
    async getCurrentUser(): Promise<Principal | null> {
      return apiFetch<Principal | null>('/api/admin/me');
    },

    async signIn(creds: SignInCredentials): Promise<Principal> {
      return apiFetch<Principal>('/api/admin/session', {
        method: 'POST',
        body: JSON.stringify(creds),
      });
    },

    async signOut(): Promise<void> {
      await apiFetch<{ ok: boolean }>('/api/admin/session', { method: 'DELETE' });
    },

    async signUp() {
      throw new Error('not supported');
    },

    async requestPasswordReset() {
      throw new Error('not supported');
    },

    async resetPassword() {
      throw new Error('not supported');
    },
  };
}

export function makeContentClient(): ModCmsContentClient {
  return {
    async list(query?: ListQuery): Promise<AdminContentEntry[]> {
      const data = await apiFetch<{ entries: AdminContentEntry[] }>(
        `/api/admin/content${listQueryString(query)}`,
      );
      return data.entries;
    },

    async put(input: ContentInput): Promise<ContentEntry> {
      return apiFetch<ContentEntry>('/api/admin/content', {
        method: 'POST',
        body: JSON.stringify(input),
      });
    },

    async trash(id: string): Promise<EntityRef> {
      return apiFetch<EntityRef>('/api/admin/trash', {
        method: 'POST',
        body: JSON.stringify({ id }),
      });
    },

    async restore(id: string): Promise<EntityRef> {
      return apiFetch<EntityRef>('/api/admin/restore', {
        method: 'POST',
        body: JSON.stringify({ id }),
      });
    },

    async remove(id: string): Promise<EntityRef> {
      return apiFetch<EntityRef>('/api/admin/remove', {
        method: 'DELETE',
        body: JSON.stringify({ id }),
      });
    },

    async publish(id: string): Promise<EntityRef> {
      return apiFetch<EntityRef>('/api/admin/publish', {
        method: 'POST',
        body: JSON.stringify({ id }),
      });
    },

    async unpublish(id: string): Promise<EntityRef> {
      return apiFetch<EntityRef>('/api/admin/unpublish', {
        method: 'POST',
        body: JSON.stringify({ id }),
      });
    },

    async schedule(id: string, at: string): Promise<EntityRef> {
      return apiFetch<EntityRef>('/api/admin/schedule', {
        method: 'POST',
        body: JSON.stringify({ id, at }),
      });
    },

    async setVisibility(id: string, visibility: ContentVisibility): Promise<EntityRef> {
      return apiFetch<EntityRef>('/api/admin/visibility', {
        method: 'POST',
        body: JSON.stringify({ id, visibility }),
      });
    },

    async translate(id: string): Promise<{ ok: boolean }> {
      return apiFetch<{ ok: boolean }>('/api/admin/translate', {
        method: 'POST',
        body: JSON.stringify({ id }),
      });
    },

    async getBySlug(_type: string, _slug: string): Promise<ContentEntry | null> {
      throw new Error('getBySlug is not supported in the admin client');
    },
  };
}
