import type {
  Apartment,
  ApartmentWithRooms,
  Room,
  RoomFull,
  Wall,
  WallOpening,
  ElectricalItem,
  FurnitureItem,
  CreateApartmentDto,
  UpdateApartmentDto,
  CreateRoomDto,
  UpdateRoomDto,
  CreateWallDto,
  CreateWallOpeningDto,
  UpdateWallOpeningDto,
  CreateElectricalItemDto,
  UpdateElectricalItemDto,
  CreateFurnitureItemDto,
  UpdateFurnitureItemDto,
  BatchSyncOpeningsDto,
  BatchSyncElectricalDto,
  BatchSyncFurnitureDto,
  BatchSyncAnnotationsDto,
  Annotation,
  ApiResponse,
  ApiListResponse,
  ApiErrorResponse,
} from '@house-plan-maker/shared';

const BASE_URL = '/api';

class ApiError extends Error {
  readonly statusCode: number;

  constructor(message: string, statusCode: number) {
    super(message);
    this.name = 'ApiError';
    this.statusCode = statusCode;
  }
}

async function request<T>(
  path: string,
  options: RequestInit = {},
): Promise<T> {
  const url = `${BASE_URL}${path}`;
  const headers: Record<string, string> = {
    ...((options.headers as Record<string, string>) ?? {}),
  };

  // Only set Content-Type for requests with a body
  if (options.body) {
    headers['Content-Type'] = 'application/json';
  }

  const response = await fetch(url, { ...options, headers });

  if (!response.ok) {
    let errorMessage = `Request failed with status ${response.status}`;
    try {
      const errorBody = (await response.json()) as ApiErrorResponse;
      errorMessage = errorBody.error ?? errorMessage;
    } catch {
      // If parsing fails, use the default message
    }
    throw new ApiError(errorMessage, response.status);
  }

  // Handle 204 No Content (e.g. DELETE responses)
  if (response.status === 204) {
    return undefined as T;
  }

  return response.json() as Promise<T>;
}

// ── Apartments ──

export async function getApartments(): Promise<readonly Apartment[]> {
  const result = await request<ApiListResponse<Apartment>>('/apartments');
  return result.data;
}

export async function getApartment(id: string): Promise<ApartmentWithRooms> {
  const result = await request<ApiResponse<ApartmentWithRooms>>(
    `/apartments/${id}`,
  );
  return result.data;
}

export async function createApartment(
  data: CreateApartmentDto,
): Promise<Apartment> {
  const result = await request<ApiResponse<Apartment>>('/apartments', {
    method: 'POST',
    body: JSON.stringify(data),
  });
  return result.data;
}

export async function updateApartment(
  id: string,
  data: UpdateApartmentDto,
): Promise<Apartment> {
  const result = await request<ApiResponse<Apartment>>(`/apartments/${id}`, {
    method: 'PUT',
    body: JSON.stringify(data),
  });
  return result.data;
}

export async function deleteApartment(id: string): Promise<void> {
  await request<void>(`/apartments/${id}`, { method: 'DELETE' });
}

// ── Rooms ──

export async function getRooms(apartmentId: string): Promise<readonly Room[]> {
  const result = await request<ApiListResponse<Room>>(
    `/apartments/${apartmentId}/rooms`,
  );
  return result.data;
}

export async function getRoomFull(roomId: string): Promise<RoomFull> {
  const result = await request<ApiResponse<RoomFull>>(
    `/rooms/${roomId}/full`,
  );
  return result.data;
}

export async function createRoom(
  apartmentId: string,
  data: CreateRoomDto,
): Promise<Room> {
  const result = await request<ApiResponse<Room>>(
    `/apartments/${apartmentId}/rooms`,
    {
      method: 'POST',
      body: JSON.stringify(data),
    },
  );
  return result.data;
}

export async function updateRoom(
  id: string,
  data: UpdateRoomDto,
): Promise<Room> {
  const result = await request<ApiResponse<Room>>(`/rooms/${id}`, {
    method: 'PUT',
    body: JSON.stringify(data),
  });
  return result.data;
}

export async function deleteRoom(id: string): Promise<void> {
  await request<void>(`/rooms/${id}`, { method: 'DELETE' });
}

export async function cloneRoom(id: string): Promise<Room> {
  const result = await request<ApiResponse<Room>>(`/rooms/${id}/clone`, {
    method: 'POST',
  });
  return result.data;
}

// ── Walls ──

export async function bulkUpdateWalls(
  roomId: string,
  walls: readonly CreateWallDto[],
): Promise<readonly Wall[]> {
  const result = await request<ApiResponse<readonly Wall[]>>(
    `/rooms/${roomId}/walls`,
    {
      method: 'PUT',
      body: JSON.stringify({ walls }),
    },
  );
  return result.data;
}

// ── Wall Openings ──

export async function createWallOpening(
  roomId: string,
  data: CreateWallOpeningDto,
): Promise<WallOpening> {
  const result = await request<ApiResponse<WallOpening>>(
    `/rooms/${roomId}/openings`,
    {
      method: 'POST',
      body: JSON.stringify(data),
    },
  );
  return result.data;
}

export async function updateWallOpening(
  roomId: string,
  openingId: string,
  data: UpdateWallOpeningDto,
): Promise<WallOpening> {
  const result = await request<ApiResponse<WallOpening>>(
    `/rooms/${roomId}/openings/${openingId}`,
    {
      method: 'PUT',
      body: JSON.stringify(data),
    },
  );
  return result.data;
}

export async function deleteWallOpening(
  roomId: string,
  openingId: string,
): Promise<void> {
  await request<void>(`/rooms/${roomId}/openings/${openingId}`, {
    method: 'DELETE',
  });
}

// ── Electrical Items ──

export async function createElectricalItem(
  roomId: string,
  data: CreateElectricalItemDto,
): Promise<ElectricalItem> {
  const result = await request<ApiResponse<ElectricalItem>>(
    `/rooms/${roomId}/electrical`,
    {
      method: 'POST',
      body: JSON.stringify(data),
    },
  );
  return result.data;
}

export async function updateElectricalItem(
  roomId: string,
  itemId: string,
  data: UpdateElectricalItemDto,
): Promise<ElectricalItem> {
  const result = await request<ApiResponse<ElectricalItem>>(
    `/electrical/${itemId}`,
    {
      method: 'PUT',
      body: JSON.stringify(data),
    },
  );
  return result.data;
}

export async function deleteElectricalItem(
  roomId: string,
  itemId: string,
): Promise<void> {
  await request<void>(`/electrical/${itemId}`, {
    method: 'DELETE',
  });
}

// ── Furniture Items ──

export async function createFurnitureItem(
  roomId: string,
  data: CreateFurnitureItemDto,
): Promise<FurnitureItem> {
  const result = await request<ApiResponse<FurnitureItem>>(
    `/rooms/${roomId}/furniture`,
    {
      method: 'POST',
      body: JSON.stringify(data),
    },
  );
  return result.data;
}

export async function updateFurnitureItem(
  roomId: string,
  itemId: string,
  data: UpdateFurnitureItemDto,
): Promise<FurnitureItem> {
  const result = await request<ApiResponse<FurnitureItem>>(
    `/furniture/${itemId}`,
    {
      method: 'PUT',
      body: JSON.stringify(data),
    },
  );
  return result.data;
}

export async function deleteFurnitureItem(
  roomId: string,
  itemId: string,
): Promise<void> {
  await request<void>(`/furniture/${itemId}`, {
    method: 'DELETE',
  });
}

// ── Batch Sync ──

export async function batchSyncOpenings(
  roomId: string,
  data: BatchSyncOpeningsDto,
): Promise<readonly WallOpening[]> {
  const result = await request<ApiListResponse<WallOpening>>(
    `/rooms/${roomId}/openings/batch`,
    {
      method: 'PUT',
      body: JSON.stringify(data),
    },
  );
  return result.data;
}

export async function batchSyncElectrical(
  roomId: string,
  data: BatchSyncElectricalDto,
): Promise<readonly ElectricalItem[]> {
  const result = await request<ApiListResponse<ElectricalItem>>(
    `/rooms/${roomId}/electrical/batch`,
    {
      method: 'PUT',
      body: JSON.stringify(data),
    },
  );
  return result.data;
}

export async function batchSyncFurniture(
  roomId: string,
  data: BatchSyncFurnitureDto,
): Promise<readonly FurnitureItem[]> {
  const result = await request<ApiListResponse<FurnitureItem>>(
    `/rooms/${roomId}/furniture/batch`,
    {
      method: 'PUT',
      body: JSON.stringify(data),
    },
  );
  return result.data;
}

export async function batchSyncAnnotations(
  roomId: string,
  data: BatchSyncAnnotationsDto,
): Promise<readonly Annotation[]> {
  const result = await request<ApiListResponse<Annotation>>(
    `/rooms/${roomId}/annotations/batch`,
    {
      method: 'PUT',
      body: JSON.stringify(data),
    },
  );
  return result.data;
}

export { ApiError };
