import { getApiBaseUrlSync } from '@/src/lib/server-config';

export type Tenant = {
  id: number;
  name: string;
  short?: string | null;
  [key: string]: unknown;
};

export type TaskStatus = 'Offen' | 'In Bearbeitung' | 'Abgeschlossen';

export type Task = {
  id: number;
  name: string;
  description?: string | null;
  categorie?: string | null;
  userId?: string | null;
  user_id?: string | null;
  profile?: string | null;
  project?: number | { id?: number; name?: string } | null;
  customer?: number | { id?: number; name?: string } | null;
  plant?: number | { id?: number; name?: string } | null;
  archived?: boolean;
  [key: string]: unknown;
};

export type TenantProfile = {
  id?: number | string;
  user_id?: string;
  full_name?: string;
  fullName?: string;
  email?: string;
  [key: string]: unknown;
};

export type StaffTimeSpan = {
  id: string | null;
  eventIds: string[];
  state: string;
  started_at: string;
  stopped_at: string | null;
  duration_minutes: number;
  user_id: string | null;
  type: string;
  description: string;
};

export type Project = {
  id: number;
  name: string;
  notes?: string | null;
  projectNumber?: string | null;
  archived?: boolean;
  [key: string]: unknown;
};

export type ProjectFile = {
  id: string;
  name?: string | null;
  path?: string | null;
  project?: number | { id?: number; name?: string };
  customer?: number | { id?: number; name?: string };
  plant?: number | { id?: number; name?: string };
  createddocument?: number | { id?: number; documentNumber?: string };
  mimeType?: string | null;
  url?: string;
  archived?: boolean;
  [key: string]: unknown;
};

export type Customer = {
  id: number;
  name: string;
  customerNumber?: string | null;
  notes?: string | null;
  archived?: boolean;
  infoData?: {
    email?: string | null;
    tel?: string | null;
    mobileTel?: string | null;
    [key: string]: unknown;
  } | null;
  [key: string]: unknown;
};

export type Plant = {
  id: number;
  name: string;
  description?: string | null;
  customer?: number | { id?: number; name?: string };
  archived?: boolean;
  [key: string]: unknown;
};

export type CustomerInventoryItem = {
  id: number;
  name: string;
  customer?: number | { id?: number; name?: string } | null;
  customerInventoryId?: string | null;
  serialNumber?: string | null;
  description?: string | null;
  manufacturer?: string | null;
  manufacturerNumber?: string | null;
  quantity?: number | null;
  archived?: boolean;
  [key: string]: unknown;
};

export type CreatedDocument = {
  id: number;
  documentNumber?: string | null;
  title?: string | null;
  type?: string | null;
  state?: string | null;
  documentDate?: string | null;
  customer?: number | { id?: number; name?: string };
  archived?: boolean;
  [key: string]: unknown;
};

export type MeResponse = {
  user: {
    id: string;
    email: string;
    must_change_password?: boolean;
    [key: string]: unknown;
  };
  tenants: Tenant[];
  activeTenant: number | string | null;
  profile: Record<string, unknown> | null;
  permissions: string[];
};

export type AuthSession = {
  token: string;
  refreshToken: string;
};

export type EncodedLabelRow = {
  dataType: 'pixels' | 'void' | 'check';
  rowNumber: number;
  repeat: number;
  rowData?: Uint8Array | number[] | Record<string, number>;
  blackPixelsCount: number;
};

export type EncodedLabelImage = {
  cols: number;
  rows: number;
  rowsData: EncodedLabelRow[];
};

export type PrintLabelResponse = {
  encoded: EncodedLabelImage;
  base64?: string;
};

export type MobilePushRegistrationInput = {
  localDeviceId: string;
  platform: 'ios' | 'android';
  providerToken: string;
  deviceLabel?: string | null;
  meta?: Record<string, unknown>;
};

export type MobilePushRegistrationResponse = {
  success: boolean;
  id?: string;
  centralDeviceId?: string;
  status?: string;
};

export type WikiTreeItem = {
  id: string;
  parentId?: string | null;
  title: string;
  isFolder?: boolean;
  isVirtual?: boolean;
  sortOrder?: number;
  entityType?: string | null;
  entityId?: number | null;
  entityUuid?: string | null;
  updatedAt?: string;
  [key: string]: unknown;
};

export type WikiPage = {
  id: string;
  title: string;
  content?: unknown;
  parentId?: string | null;
  isFolder?: boolean;
  entityType?: string | null;
  entityId?: number | null;
  entityUuid?: string | null;
  updatedAt?: string;
  [key: string]: unknown;
};

type RequestOptions = {
  method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
  token?: string | null;
  body?: unknown;
  timeoutMs?: number;
};

export class ApiError extends Error {
  constructor(
    message: string,
    public readonly status: number
  ) {
    super(message);
    this.name = 'ApiError';
  }
}

export function isAuthenticationError(error: unknown): boolean {
  return error instanceof ApiError && error.status === 401;
}

export type MatrixStatus = {
  enabled?: boolean;
  ready?: boolean;
  configured?: boolean;
  homeserverUrl?: string | null;
  [key: string]: unknown;
};

export type MatrixIdentity = {
  matrixUserId: string;
  displayName?: string | null;
};

export type MatrixRoom = {
  key: string;
  name: string;
  topic?: string | null;
  type?: 'room' | 'project' | 'direct' | string;
  group?: string;
  roomId?: string | null;
  alias?: string | null;
  exists?: boolean;
  projectId?: number;
  projectNumber?: string | null;
  userId?: string;
  email?: string | null;
  entityType?: string | null;
  entityId?: number | null;
  entityUuid?: string | null;
  unread?: number;
  mentions?: number;
  provisionEndpoint?: string;
  [key: string]: unknown;
};

export type MatrixAttachment = {
  fileName?: string | null;
  mimeType?: string | null;
  size?: number | null;
  mxcUri?: string | null;
  previewUrl?: string | null;
  downloadUrl?: string | null;
};

export type MatrixReaction = {
  key: string;
  count?: number;
  own?: boolean;
  senders?: string[];
  [key: string]: unknown;
};

export type MatrixMessage = {
  id: string;
  sender: string;
  senderDisplayName?: string | null;
  body?: string | null;
  timestamp?: string | number | null;
  own?: boolean;
  edited?: boolean;
  redacted?: boolean;
  msgtype?: string;
  attachment?: MatrixAttachment | null;
  replyToEventId?: string | null;
  reactions?: MatrixReaction[];
  [key: string]: unknown;
};

export type MatrixMember = {
  matrixUserId: string;
  displayName?: string | null;
  avatarUrl?: string | null;
  membership?: string;
  [key: string]: unknown;
};

export type MatrixUser = {
  userId: string;
  matrixUserId: string;
  displayName?: string | null;
  email?: string | null;
  [key: string]: unknown;
};

export type MatrixSyncResponse = {
  nextBatch?: string;
  messages?: MatrixMessage[];
  replacements?: MatrixMessage[];
  reactions?: (MatrixReaction & { targetEventId?: string })[];
  redactions?: { redacts?: string; eventId?: string; targetEventId?: string }[];
  members?: MatrixMember[];
  [key: string]: unknown;
};

export type MatrixUnreadCounts = Record<string, { count?: number; mentions?: number }>;

function buildUrl(path: string): string {
  if (path.startsWith('http://') || path.startsWith('https://')) {
    return path;
  }

  const normalizedPath = path.startsWith('/') ? path : `/${path}`;
  return `${getApiBaseUrlSync()}${normalizedPath}`;
}

async function parseJson(response: Response): Promise<unknown> {
  const text = await response.text();
  if (!text) return null;

  try {
    return JSON.parse(text);
  } catch {
    return null;
  }
}

const DEFAULT_REQUEST_TIMEOUT_MS = 15000;

function createTimeoutSignal(timeoutMs = DEFAULT_REQUEST_TIMEOUT_MS): { signal: AbortSignal; cleanup: () => void } {
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), timeoutMs);

  return {
    signal: controller.signal,
    cleanup: () => clearTimeout(timeout),
  };
}

function isAbortError(error: unknown): boolean {
  return error instanceof Error && error.name === 'AbortError';
}

export async function apiRequest<T>(path: string, options: RequestOptions = {}): Promise<T> {
  const hasJsonBody = options.body !== undefined;
  const { signal, cleanup } = createTimeoutSignal(options.timeoutMs);
  let response: Response;

  try {
    response = await fetch(buildUrl(path), {
      method: options.method || 'GET',
      headers: {
        Accept: 'application/json',
        ...(hasJsonBody ? { 'Content-Type': 'application/json' } : {}),
        ...(options.token ? { Authorization: `Bearer ${options.token}` } : {}),
      },
      body: hasJsonBody ? JSON.stringify(options.body) : undefined,
      signal,
    });
  } catch (error) {
    if (isAbortError(error)) {
      throw new Error(`Zeitüberschreitung beim Verbinden mit dem FEDEO-Server (${path}).`);
    }
    throw error;
  } finally {
    cleanup();
  }

  const payload = await parseJson(response);

  if (!response.ok) {
    const message =
      (payload as { message?: string; error?: string } | null)?.message ||
      (payload as { message?: string; error?: string } | null)?.error ||
      `Request failed (${response.status}) for ${path}`;
    throw new ApiError(message, response.status);
  }

  return payload as T;
}

async function apiFormRequest<T>(path: string, token: string, formData: FormData): Promise<T> {
  const { signal, cleanup } = createTimeoutSignal();
  let response: Response;

  try {
    response = await fetch(buildUrl(path), {
      method: 'POST',
      headers: {
        Accept: 'application/json',
        Authorization: `Bearer ${token}`,
      },
      body: formData,
      signal,
    });
  } catch (error) {
    if (isAbortError(error)) {
      throw new Error(`Zeitüberschreitung beim Hochladen zum FEDEO-Server (${path}).`);
    }
    throw error;
  } finally {
    cleanup();
  }

  const payload = await parseJson(response);

  if (!response.ok) {
    const message =
      (payload as { message?: string; error?: string } | null)?.message ||
      (payload as { message?: string; error?: string } | null)?.error ||
      `Request failed (${response.status}) for ${path}`;
    throw new ApiError(message, response.status);
  }

  return payload as T;
}

function matrixRoomPath(roomKey: string, suffix = ''): string {
  return `/api/communication/matrix/rooms/${encodeURIComponent(roomKey)}${suffix}`;
}

export async function checkBackendHealth(): Promise<{ status: string; [key: string]: unknown }> {
  return apiRequest<{ status: string; [key: string]: unknown }>('/health');
}

export async function fetchMatrixStatus(token: string): Promise<MatrixStatus> {
  return apiRequest<MatrixStatus>('/api/communication/matrix/status', { token });
}

export async function fetchMatrixIdentity(token: string): Promise<MatrixIdentity> {
  return apiRequest<MatrixIdentity>('/api/communication/matrix/me', { token });
}

export async function provisionMatrixUser(token: string): Promise<MatrixIdentity> {
  return apiRequest<MatrixIdentity>('/api/communication/matrix/me/provision', {
    method: 'POST',
    token,
  });
}

export async function fetchMatrixRooms(token: string): Promise<MatrixRoom[]> {
  const [rooms, projectRooms, directRooms, unread] = await Promise.all([
    apiRequest<{ rooms?: MatrixRoom[] }>('/api/communication/matrix/rooms', { token }),
    apiRequest<{ rooms?: MatrixRoom[] }>('/api/communication/matrix/project-rooms', { token }),
    apiRequest<{ rooms?: MatrixRoom[] }>('/api/communication/matrix/direct-rooms', { token }),
    apiRequest<{ rooms?: Record<string, { count?: number; mentions?: number }> }>('/api/communication/matrix/unread', {
      token,
    }),
  ]);

  const unreadByRoom = unread.rooms || {};
  const decorate = (room: MatrixRoom, group: string): MatrixRoom => ({
    ...room,
    group,
    unread: unreadByRoom[room.key]?.count || 0,
    mentions: unreadByRoom[room.key]?.mentions || 0,
  });

  return [
    ...(rooms.rooms || []).map((room) => decorate(room, 'Räume')),
    ...(projectRooms.rooms || []).map((room) => decorate(room, 'Projekte')),
    ...(directRooms.rooms || []).map((room) => decorate(room, 'Direkt')),
  ];
}

export async function fetchMatrixUnreadCounts(token: string): Promise<MatrixUnreadCounts> {
  const response = await apiRequest<{ rooms?: MatrixUnreadCounts }>('/api/communication/matrix/unread', { token });
  return response.rooms || {};
}

export async function fetchMatrixUsers(token: string): Promise<MatrixUser[]> {
  const response = await apiRequest<{ users?: MatrixUser[] }>('/api/communication/matrix/users', { token });
  return response.users || [];
}

export async function createMatrixRoom(
  token: string,
  payload: { key: string; name: string; topic?: string | null; type?: string }
): Promise<MatrixRoom> {
  return apiRequest<MatrixRoom>('/api/communication/matrix/rooms', {
    method: 'POST',
    token,
    body: payload,
  });
}

export async function provisionMatrixRoom(token: string, room: MatrixRoom): Promise<MatrixRoom> {
  if (room.provisionEndpoint) {
    return apiRequest<MatrixRoom>(room.provisionEndpoint, { method: 'POST', token });
  }

  if (room.type === 'project' && room.projectId) {
    return apiRequest<MatrixRoom>(`/api/communication/matrix/project-rooms/${room.projectId}/provision`, {
      method: 'POST',
      token,
    });
  }

  if (room.type === 'direct' && room.userId) {
    return apiRequest<MatrixRoom>(`/api/communication/matrix/direct-rooms/${encodeURIComponent(room.userId)}/provision`, {
      method: 'POST',
      token,
    });
  }

  return apiRequest<MatrixRoom>(matrixRoomPath(room.key, '/provision'), {
    method: 'POST',
    token,
    body: {
      key: room.key,
      name: room.name,
      topic: room.topic,
      type: room.type || 'room',
      entityType: room.entityType,
      entityId: room.entityId,
      entityUuid: room.entityUuid,
    },
  });
}

export async function fetchMatrixMessages(token: string, roomKey: string): Promise<MatrixMessage[]> {
  const response = await apiRequest<{ messages?: MatrixMessage[] }>(matrixRoomPath(roomKey, '/messages'), { token });
  return response.messages || [];
}

export async function syncMatrixRoom(
  token: string,
  roomKey: string,
  since?: string,
  initial = false
): Promise<MatrixSyncResponse> {
  const query = new URLSearchParams();
  if (since) query.set('since', since);
  if (initial) query.set('initial', '1');
  const suffix = query.toString() ? `/sync?${query.toString()}` : '/sync';
  return apiRequest<MatrixSyncResponse>(matrixRoomPath(roomKey, suffix), { token });
}

export async function fetchMatrixMembers(token: string, roomKey: string): Promise<MatrixMember[]> {
  const response = await apiRequest<{ members?: MatrixMember[] }>(matrixRoomPath(roomKey, '/members'), { token });
  return response.members || [];
}

export async function sendMatrixMessage(
  token: string,
  roomKey: string,
  text: string,
  replyToEventId?: string | null
): Promise<MatrixMessage> {
  return apiRequest<MatrixMessage>(matrixRoomPath(roomKey, '/messages'), {
    method: 'POST',
    token,
    body: { text, replyToEventId },
  });
}

export async function editMatrixMessage(token: string, roomKey: string, eventId: string, text: string): Promise<MatrixMessage> {
  return apiRequest<MatrixMessage>(matrixRoomPath(roomKey, `/messages/${encodeURIComponent(eventId)}`), {
    method: 'PUT',
    token,
    body: { text },
  });
}

export async function deleteMatrixMessage(token: string, roomKey: string, eventId: string): Promise<void> {
  await apiRequest(matrixRoomPath(roomKey, `/messages/${encodeURIComponent(eventId)}`), {
    method: 'DELETE',
    token,
  });
}

export async function reactToMatrixMessage(token: string, roomKey: string, eventId: string, key: string): Promise<void> {
  await apiRequest(matrixRoomPath(roomKey, `/messages/${encodeURIComponent(eventId)}/reactions`), {
    method: 'POST',
    token,
    body: { key },
  });
}

export async function markMatrixRoomRead(token: string, roomKey: string, eventId?: string): Promise<void> {
  await apiRequest(matrixRoomPath(roomKey, '/read'), {
    method: 'POST',
    token,
    body: { eventId },
  });
}

export async function syncMatrixMembers(token: string, roomKey: string): Promise<void> {
  await apiRequest(matrixRoomPath(roomKey, '/members/sync'), {
    method: 'POST',
    token,
  });
}

export async function inviteMatrixMember(token: string, roomKey: string, userId: string): Promise<void> {
  await apiRequest(matrixRoomPath(roomKey, '/members/invite'), {
    method: 'POST',
    token,
    body: { userId },
  });
}

export async function removeMatrixMember(token: string, roomKey: string, matrixUserId: string): Promise<void> {
  await apiRequest(matrixRoomPath(roomKey, `/members/${encodeURIComponent(matrixUserId)}`), {
    method: 'DELETE',
    token,
  });
}

export async function uploadMatrixAttachment(
  token: string,
  roomKey: string,
  file: { uri: string; name: string; mimeType?: string | null }
): Promise<MatrixMessage> {
  const formData = new FormData();
  formData.append('file', {
    uri: file.uri,
    name: file.name,
    type: file.mimeType || 'application/octet-stream',
  } as unknown as Blob);

  return apiFormRequest<MatrixMessage>(matrixRoomPath(roomKey, '/attachments'), token, formData);
}

export async function renderPrintLabel(
  token: string,
  context: Record<string, unknown>,
  width = 584,
  height = 354
): Promise<PrintLabelResponse> {
  return apiRequest<PrintLabelResponse>('/api/print/label', {
    method: 'POST',
    token,
    body: {
      context,
      width,
      height,
    },
  });
}

function requireAuthSession(payload: Partial<AuthSession>, action: string): AuthSession {
  if (!payload?.token || !payload?.refreshToken) {
    throw new Error(`${action} did not return a complete session.`);
  }

  return { token: payload.token, refreshToken: payload.refreshToken };
}

export async function loginWithEmailPassword(email: string, password: string): Promise<AuthSession> {
  const payload = await apiRequest<Partial<AuthSession>>('/auth/login', {
    method: 'POST',
    body: { email, password },
  });

  return requireAuthSession(payload, 'Login');
}

export async function refreshAuthSession(refreshToken: string): Promise<AuthSession> {
  const payload = await apiRequest<Partial<AuthSession>>('/auth/refresh', {
    method: 'POST',
    body: { refreshToken },
  });

  return requireAuthSession(payload, 'Session refresh');
}

export async function logoutSession(refreshToken: string): Promise<void> {
  await apiRequest('/auth/logout', {
    method: 'POST',
    body: { refreshToken },
  });
}

export async function fetchMe(token: string): Promise<MeResponse> {
  return apiRequest<MeResponse>('/api/me', {
    token,
  });
}

export async function registerMobilePushDevice(
  token: string,
  payload: MobilePushRegistrationInput
): Promise<MobilePushRegistrationResponse> {
  return apiRequest<MobilePushRegistrationResponse>('/api/notifications/push/mobile/register', {
    method: 'POST',
    token,
    body: payload,
  });
}

export async function sendMobileTestPush(token: string): Promise<{ accepted: number; rejected: number; deliveryJobId: string }> {
  return apiRequest<{ accepted: number; rejected: number; deliveryJobId: string }>('/api/notifications/test-mobile-push', {
    method: 'POST',
    token,
  });
}

export async function switchTenantRequest(
  tenantId: number,
  token: string,
  refreshToken: string
): Promise<AuthSession> {
  const payload = await apiRequest<Partial<AuthSession>>('/api/tenant/switch', {
    method: 'POST',
    token,
    body: { tenant_id: String(tenantId), refreshToken },
  });

  return requireAuthSession(payload, 'Tenant switch');
}

export async function fetchTasks(token: string): Promise<Task[]> {
  const tasks = await apiRequest<Task[]>('/api/resource/tasks', { token });
  return (tasks || []).filter((task) => !task.archived);
}

export async function createTask(
  token: string,
  payload: {
    name: string;
    description?: string | null;
    categorie?: TaskStatus;
    userId?: string | null;
    project?: number | null;
    customer?: number | null;
    plant?: number | null;
  }
): Promise<Task> {
  return apiRequest<Task>('/api/resource/tasks', {
    method: 'POST',
    token,
    body: payload,
  });
}

export async function updateTask(token: string, taskId: number, payload: Partial<Task>): Promise<Task> {
  return apiRequest<Task>(`/api/resource/tasks/${taskId}`, {
    method: 'PUT',
    token,
    body: payload,
  });
}

export async function fetchTenantProfiles(token: string): Promise<TenantProfile[]> {
  const response = await apiRequest<{ data?: TenantProfile[] }>('/api/tenant/profiles', { token });
  return response?.data || [];
}

export async function fetchStaffTimeSpans(
  token: string,
  targetUserId?: string
): Promise<StaffTimeSpan[]> {
  const query = targetUserId ? `?targetUserId=${encodeURIComponent(targetUserId)}` : '';
  const spans = await apiRequest<any[]>(`/api/staff/time/spans${query}`, { token });

  return (spans || [])
    .map((span) => {
      const started = span.startedAt ? new Date(span.startedAt) : null;
      const ended = span.endedAt ? new Date(span.endedAt) : new Date();
      const durationMinutes =
        started && ended ? Math.max(0, Math.floor((ended.getTime() - started.getTime()) / 60000)) : 0;

      return {
        id: span.sourceEventIds?.[0] ?? null,
        eventIds: span.sourceEventIds || [],
        state: span.status || 'draft',
        started_at: span.startedAt,
        stopped_at: span.endedAt || null,
        duration_minutes: durationMinutes,
        user_id: targetUserId || null,
        type: span.type || 'work',
        description: span.payload?.description || '',
      } as StaffTimeSpan;
    })
    .sort((a, b) => new Date(b.started_at).getTime() - new Date(a.started_at).getTime());
}

export async function createStaffTimeEvent(
  token: string,
  payload: {
    eventtype: string;
    eventtime: string;
    user_id: string;
    description?: string;
  }
): Promise<void> {
  await apiRequest('/api/staff/time/event', {
    method: 'POST',
    token,
    body: {
      eventtype: payload.eventtype,
      eventtime: payload.eventtime,
      user_id: payload.user_id,
      payload: payload.description ? { description: payload.description } : undefined,
    },
  });
}

export async function submitStaffTime(token: string, eventIds: string[]): Promise<void> {
  await apiRequest('/api/staff/time/submit', {
    method: 'POST',
    token,
    body: { eventIds },
  });
}

export async function approveStaffTime(
  token: string,
  eventIds: string[],
  employeeUserId: string
): Promise<void> {
  await apiRequest('/api/staff/time/approve', {
    method: 'POST',
    token,
    body: { eventIds, employeeUserId },
  });
}

export async function rejectStaffTime(
  token: string,
  eventIds: string[],
  employeeUserId: string,
  reason: string
): Promise<void> {
  await apiRequest('/api/staff/time/reject', {
    method: 'POST',
    token,
    body: { eventIds, employeeUserId, reason },
  });
}

export async function fetchProjects(token: string, includeArchived = false): Promise<Project[]> {
  const projects = await apiRequest<Project[]>('/api/resource/projects', { token });
  if (includeArchived) return projects || [];
  return (projects || []).filter((project) => !project.archived);
}

export async function createProject(
  token: string,
  payload: {
    name: string;
    projectNumber?: string | null;
    customer?: number | null;
    plant?: number | null;
    notes?: string | null;
  }
): Promise<Project> {
  return apiRequest<Project>('/api/resource/projects', {
    method: 'POST',
    token,
    body: payload,
  });
}

export async function fetchCustomers(token: string, includeArchived = false): Promise<Customer[]> {
  const customers = await apiRequest<Customer[]>('/api/resource/customers', { token });
  if (includeArchived) return customers || [];
  return (customers || []).filter((customer) => !customer.archived);
}

export async function createCustomer(
  token: string,
  payload: {
    name: string;
    customerNumber?: string | null;
    notes?: string | null;
  }
): Promise<Customer> {
  return apiRequest<Customer>('/api/resource/customers', {
    method: 'POST',
    token,
    body: payload,
  });
}

export async function fetchCustomerById(token: string, customerId: number): Promise<Customer> {
  return apiRequest<Customer>(`/api/resource/customers/${customerId}`, { token });
}

function resolveCustomerIdFromCustomerInventoryItem(item: CustomerInventoryItem): number | null {
  const rawCustomer = item.customer;
  if (!rawCustomer) return null;
  if (typeof rawCustomer === 'object') {
    return rawCustomer.id ? Number(rawCustomer.id) : null;
  }
  return Number(rawCustomer);
}

export async function fetchCustomerInventoryItems(
  token: string,
  customerId: number,
  includeArchived = false
): Promise<CustomerInventoryItem[]> {
  const rows = await apiRequest<CustomerInventoryItem[]>('/api/resource/customerinventoryitems', { token });

  return (rows || []).filter((item) => {
    if (!includeArchived && item.archived) return false;
    return resolveCustomerIdFromCustomerInventoryItem(item) === Number(customerId);
  });
}

export async function fetchAllCustomerInventoryItems(
  token: string,
  includeArchived = false
): Promise<CustomerInventoryItem[]> {
  const rows = await apiRequest<CustomerInventoryItem[]>('/api/resource/customerinventoryitems', { token });
  if (includeArchived) return rows || [];
  return (rows || []).filter((item) => !item.archived);
}

export async function createCustomerInventoryItem(
  token: string,
  payload: {
    customer: number;
    name: string;
    customerInventoryId?: string | null;
    serialNumber?: string | null;
    description?: string | null;
    quantity?: number | null;
  }
): Promise<CustomerInventoryItem> {
  const autoInventoryId = `MOB-${Date.now()}`;

  return apiRequest<CustomerInventoryItem>('/api/resource/customerinventoryitems', {
    method: 'POST',
    token,
    body: {
      customer: payload.customer,
      name: payload.name,
      customerInventoryId: payload.customerInventoryId?.trim() || autoInventoryId,
      serialNumber: payload.serialNumber?.trim() || null,
      description: payload.description?.trim() || null,
      quantity: Number.isFinite(Number(payload.quantity)) ? Number(payload.quantity) : 1,
    },
  });
}

export async function fetchPlants(token: string, includeArchived = false): Promise<Plant[]> {
  const plants = await apiRequest<(Plant & { description?: unknown })[]>('/api/resource/plants', { token });
  const normalized = (plants || []).map((plant) => {
    const legacyDescription = typeof plant.description === 'string'
      ? plant.description
      : (plant.description && typeof plant.description === 'object' && 'text' in (plant.description as Record<string, unknown>)
          ? String((plant.description as Record<string, unknown>).text || '')
          : null);

    return {
      ...plant,
      description: legacyDescription || null,
    } as Plant;
  });

  if (includeArchived) return normalized;
  return normalized.filter((plant) => !plant.archived);
}

export async function createPlant(
  token: string,
  payload: {
    name: string;
    description?: string | null;
    customer?: number | null;
  }
): Promise<Plant> {
  return apiRequest<Plant>('/api/resource/plants', {
    method: 'POST',
    token,
    body: {
      name: payload.name,
      customer: payload.customer ?? null,
      description: {
        text: payload.description?.trim() || '',
        html: '',
        json: [],
      },
    },
  });
}

export async function fetchPlantById(token: string, plantId: number): Promise<Plant> {
  return apiRequest<Plant>(`/api/resource/plants/${plantId}`, { token });
}

function toQueryString(params: Record<string, unknown>): string {
  const searchParams = new URLSearchParams();
  Object.entries(params).forEach(([key, value]) => {
    if (value === undefined || value === null || value === '') return;
    searchParams.append(key, String(value));
  });
  const query = searchParams.toString();
  return query ? `?${query}` : '';
}

export async function fetchWikiTree(
  token: string,
  filters: {
    entityType?: string;
    entityId?: number | null;
    entityUuid?: string | null;
  } = {}
): Promise<WikiTreeItem[]> {
  const query = toQueryString({
    entityType: filters.entityType,
    entityId: filters.entityId,
    entityUuid: filters.entityUuid,
  });
  return apiRequest<WikiTreeItem[]>(`/api/wiki/tree${query}`, { token });
}

export async function fetchWikiPageById(token: string, pageId: string): Promise<WikiPage> {
  return apiRequest<WikiPage>(`/api/wiki/${encodeURIComponent(pageId)}`, { token });
}

export async function createWikiPage(
  token: string,
  payload: {
    title: string;
    parentId?: string | null;
    isFolder?: boolean;
    entityType?: string;
    entityId?: number | null;
    entityUuid?: string | null;
  }
): Promise<WikiPage> {
  return apiRequest<WikiPage>('/api/wiki', {
    method: 'POST',
    token,
    body: payload,
  });
}

export async function updateWikiPage(
  token: string,
  pageId: string,
  payload: {
    title?: string;
    content?: unknown;
    parentId?: string | null;
    sortOrder?: number;
    isFolder?: boolean;
  }
): Promise<WikiPage> {
  return apiRequest<WikiPage>(`/api/wiki/${encodeURIComponent(pageId)}`, {
    method: 'PATCH',
    token,
    body: payload,
  });
}

export async function deleteWikiPage(token: string, pageId: string): Promise<{ success: boolean; deletedId?: string }> {
  return apiRequest<{ success: boolean; deletedId?: string }>(`/api/wiki/${encodeURIComponent(pageId)}`, {
    method: 'DELETE',
    token,
  });
}

export async function fetchProjectById(token: string, projectId: number): Promise<Project> {
  return apiRequest<Project>(`/api/resource/projects/${projectId}`, { token });
}

function resolveProjectIdFromTask(task: Task): number | null {
  const rawProject = task.project;
  if (!rawProject) return null;
  if (typeof rawProject === 'object') {
    return rawProject.id ? Number(rawProject.id) : null;
  }
  return Number(rawProject);
}

export async function fetchProjectTasks(token: string, projectId: number): Promise<Task[]> {
  const tasks = await fetchTasks(token);
  return (tasks || []).filter((task) => resolveProjectIdFromTask(task) === Number(projectId));
}

export async function createProjectTask(
  token: string,
  payload: {
    projectId: number;
    name: string;
    description?: string | null;
    userId?: string | null;
    categorie?: TaskStatus;
  }
): Promise<Task> {
  return createTask(token, {
    name: payload.name,
    description: payload.description || null,
    userId: payload.userId || null,
    categorie: payload.categorie || 'Offen',
    project: payload.projectId,
  });
}

function resolveProjectIdFromFile(file: ProjectFile): number | null {
  const rawProject = file.project;
  if (!rawProject) return null;
  if (typeof rawProject === 'object') {
    return rawProject.id ? Number(rawProject.id) : null;
  }
  return Number(rawProject);
}

function resolveCustomerIdFromFile(file: ProjectFile): number | null {
  const rawCustomer = file.customer;
  if (!rawCustomer) return null;
  if (typeof rawCustomer === 'object') {
    return rawCustomer.id ? Number(rawCustomer.id) : null;
  }
  return Number(rawCustomer);
}

function resolvePlantIdFromFile(file: ProjectFile): number | null {
  const rawPlant = file.plant;
  if (!rawPlant) return null;
  if (typeof rawPlant === 'object') {
    return rawPlant.id ? Number(rawPlant.id) : null;
  }
  return Number(rawPlant);
}

function resolveCreatedDocumentIdFromFile(file: ProjectFile): number | null {
  const rawCreatedDocument = file.createddocument;
  if (!rawCreatedDocument) return null;
  if (typeof rawCreatedDocument === 'object') {
    return rawCreatedDocument.id ? Number(rawCreatedDocument.id) : null;
  }
  return Number(rawCreatedDocument);
}

function resolveCustomerIdFromCreatedDocument(doc: CreatedDocument): number | null {
  const rawCustomer = doc.customer;
  if (!rawCustomer) return null;
  if (typeof rawCustomer === 'object') {
    return rawCustomer.id ? Number(rawCustomer.id) : null;
  }
  return Number(rawCustomer);
}

export async function fetchProjectFiles(token: string, projectId: number): Promise<ProjectFile[]> {
  const files = await apiRequest<ProjectFile[]>('/api/resource/files', { token });
  const projectFiles = (files || []).filter((file) => {
    if (file.archived) return false;
    return resolveProjectIdFromFile(file) === Number(projectId);
  });

  if (projectFiles.length === 0) return [];

  const presigned = await apiRequest<{ files?: ProjectFile[] }>('/api/files/presigned', {
    method: 'POST',
    token,
    body: { ids: projectFiles.map((file) => file.id) },
  });

  return presigned.files || [];
}

export async function fetchCustomerFiles(token: string, customerId: number): Promise<ProjectFile[]> {
  const files = await apiRequest<ProjectFile[]>('/api/resource/files', { token });
  const customerFiles = (files || []).filter((file) => {
    if (file.archived) return false;
    return resolveCustomerIdFromFile(file) === Number(customerId);
  });

  if (customerFiles.length === 0) return [];

  const presigned = await apiRequest<{ files?: ProjectFile[] }>('/api/files/presigned', {
    method: 'POST',
    token,
    body: { ids: customerFiles.map((file) => file.id) },
  });

  return presigned.files || [];
}

export async function fetchPlantFiles(token: string, plantId: number): Promise<ProjectFile[]> {
  const files = await apiRequest<ProjectFile[]>('/api/resource/files', { token });
  const plantFiles = (files || []).filter((file) => {
    if (file.archived) return false;
    return resolvePlantIdFromFile(file) === Number(plantId);
  });

  if (plantFiles.length === 0) return [];

  const presigned = await apiRequest<{ files?: ProjectFile[] }>('/api/files/presigned', {
    method: 'POST',
    token,
    body: { ids: plantFiles.map((file) => file.id) },
  });

  return presigned.files || [];
}

export async function fetchCustomerCreatedDocuments(
  token: string,
  customerId: number
): Promise<CreatedDocument[]> {
  const docs = await apiRequest<CreatedDocument[]>('/api/resource/createddocuments', { token });

  return (docs || [])
    .filter((doc) => !doc.archived && resolveCustomerIdFromCreatedDocument(doc) === Number(customerId))
    .sort((a, b) => {
      const dateA = new Date(String(a.documentDate || '')).getTime();
      const dateB = new Date(String(b.documentDate || '')).getTime();
      return dateB - dateA;
    });
}

export async function fetchCreatedDocumentFiles(
  token: string,
  createdDocumentId: number
): Promise<ProjectFile[]> {
  const files = await apiRequest<ProjectFile[]>('/api/resource/files', { token });
  const createdDocumentFiles = (files || []).filter((file) => {
    if (file.archived) return false;
    return resolveCreatedDocumentIdFromFile(file) === Number(createdDocumentId);
  });

  if (createdDocumentFiles.length === 0) return [];

  const presigned = await apiRequest<{ files?: ProjectFile[] }>('/api/files/presigned', {
    method: 'POST',
    token,
    body: { ids: createdDocumentFiles.map((file) => file.id) },
  });

  return presigned.files || [];
}

export async function uploadProjectFile(
  token: string,
  payload: {
    projectId: number;
    uri: string;
    filename: string;
    mimeType?: string;
  }
): Promise<ProjectFile> {
  const formData = new FormData();
  formData.append(
    'file',
    {
      uri: payload.uri,
      name: payload.filename,
      type: payload.mimeType || 'application/octet-stream',
    } as any
  );
  formData.append(
    'meta',
    JSON.stringify({
      project: payload.projectId,
      name: payload.filename,
      mimeType: payload.mimeType || 'application/octet-stream',
    })
  );

  const response = await fetch(buildUrl('/api/files/upload'), {
    method: 'POST',
    headers: {
      ...(token ? { Authorization: `Bearer ${token}` } : {}),
      Accept: 'application/json',
    },
    body: formData,
  });

  const parsed = await parseJson(response);
  if (!response.ok) {
    const message =
      (parsed as { message?: string; error?: string } | null)?.message ||
      (parsed as { message?: string; error?: string } | null)?.error ||
      'Upload fehlgeschlagen.';
    throw new Error(message);
  }

  return parsed as ProjectFile;
}

export async function uploadCustomerFile(
  token: string,
  payload: {
    customerId: number;
    uri: string;
    filename: string;
    mimeType?: string;
  }
): Promise<ProjectFile> {
  const formData = new FormData();
  formData.append(
    'file',
    {
      uri: payload.uri,
      name: payload.filename,
      type: payload.mimeType || 'application/octet-stream',
    } as any
  );
  formData.append(
    'meta',
    JSON.stringify({
      customer: payload.customerId,
      name: payload.filename,
      mimeType: payload.mimeType || 'application/octet-stream',
    })
  );

  const response = await fetch(buildUrl('/api/files/upload'), {
    method: 'POST',
    headers: {
      ...(token ? { Authorization: `Bearer ${token}` } : {}),
      Accept: 'application/json',
    },
    body: formData,
  });

  const parsed = await parseJson(response);
  if (!response.ok) {
    const message =
      (parsed as { message?: string; error?: string } | null)?.message ||
      (parsed as { message?: string; error?: string } | null)?.error ||
      'Upload fehlgeschlagen.';
    throw new Error(message);
  }

  return parsed as ProjectFile;
}

export async function uploadPlantFile(
  token: string,
  payload: {
    plantId: number;
    uri: string;
    filename: string;
    mimeType?: string;
  }
): Promise<ProjectFile> {
  const formData = new FormData();
  formData.append(
    'file',
    {
      uri: payload.uri,
      name: payload.filename,
      type: payload.mimeType || 'application/octet-stream',
    } as any
  );
  formData.append(
    'meta',
    JSON.stringify({
      plant: payload.plantId,
      name: payload.filename,
      mimeType: payload.mimeType || 'application/octet-stream',
    })
  );

  const response = await fetch(buildUrl('/api/files/upload'), {
    method: 'POST',
    headers: {
      ...(token ? { Authorization: `Bearer ${token}` } : {}),
      Accept: 'application/json',
    },
    body: formData,
  });

  const parsed = await parseJson(response);
  if (!response.ok) {
    const message =
      (parsed as { message?: string; error?: string } | null)?.message ||
      (parsed as { message?: string; error?: string } | null)?.error ||
      'Upload fehlgeschlagen.';
    throw new Error(message);
  }

  return parsed as ProjectFile;
}
