import type {
  ApiEnvelope,
  CategoryRow,
  IndicatorRow,
  MasterDataFormValue,
  StatusRow,
} from "../types/master-data-types";

const API_BASE_URL =
  process.env.NEXT_PUBLIC_API_URL?.replace(/\/$/, "") ||
  "http://localhost:3001/api/v1";

const CATEGORY_URL = `${API_BASE_URL}/working-group-issues/categories`;
const INDICATOR_URL = `${API_BASE_URL}/indicators`;

// Change this one constant only if your Issue Status GET path is different.
const STATUS_URL = `${API_BASE_URL}/working-group-issues/statuses`;

function getAccessToken() {
  if (typeof window === "undefined") {
    return null;
  }

  return (
    localStorage.getItem("accessToken") ||
    localStorage.getItem("access_token") ||
    localStorage.getItem("token")
  );
}

function headers(withJsonBody = false): HeadersInit {
  const token = getAccessToken();

  return {
    Accept: "application/json",
    ...(withJsonBody ? { "Content-Type": "application/json" } : {}),
    ...(token ? { Authorization: `Bearer ${token}` } : {}),
  };
}

async function apiRequest<T>(
  url: string,
  options: RequestInit = {},
): Promise<T> {
  const response = await fetch(url, {
    ...options,
    cache: "no-store",
    credentials: "include",
  });

  let payload: unknown = null;

  try {
    payload = await response.json();
  } catch {
    payload = null;
  }

  if (!response.ok) {
    let message = `Request failed with status ${response.status}`;

    if (
      payload &&
      typeof payload === "object" &&
      "message" in payload &&
      typeof (payload as { message?: unknown }).message === "string"
    ) {
      message = (payload as { message: string }).message;
    }

    throw new Error(message);
  }

  return payload as T;
}

function asRecord(value: unknown): Record<string, unknown> | null {
  if (!value || typeof value !== "object" || Array.isArray(value)) {
    return null;
  }

  return value as Record<string, unknown>;
}

function toNullableString(value: unknown): string | null {
  if (value === null || value === undefined || value === "") {
    return null;
  }

  return String(value);
}

function normalizeCategory(value: unknown): CategoryRow | null {
  const row = asRecord(value);

  if (!row) {
    return null;
  }

  const id = Number(row.id);

  if (!Number.isFinite(id)) {
    return null;
  }

  return {
    id,
    name: String(row.name ?? ""),
    userId:
      row.userId === null || row.userId === undefined
        ? null
        : Number(row.userId),
    createdAt: toNullableString(row.createdAt),
    updatedAt: toNullableString(row.updatedAt),
    deletedAt: toNullableString(row.deletedAt),
  };
}

function normalizeIndicator(value: unknown): IndicatorRow | null {
  const row = asRecord(value);

  if (!row) {
    return null;
  }

  const id = Number(row.id);

  if (!Number.isFinite(id)) {
    return null;
  }

  return {
    id,
    name: String(row.name ?? ""),
    description: toNullableString(row.description),
    rgcDecisionCount:
      row.rgcDecisionCount === null || row.rgcDecisionCount === undefined
        ? undefined
        : Number(row.rgcDecisionCount),
    createdAt: toNullableString(row.createdAt),
    updatedAt: toNullableString(row.updatedAt),
  };
}

function normalizeStatus(value: unknown): StatusRow | null {
  const row = asRecord(value);

  if (!row) {
    return null;
  }

  const id = Number(row.id);

  if (!Number.isFinite(id)) {
    return null;
  }

  return {
    id,
    name: String(row.name ?? ""),
    code: toNullableString(row.code),
    description: toNullableString(row.description),
  };
}

export async function getMasterDataCategories(): Promise<CategoryRow[]> {
  const response = await apiRequest<ApiEnvelope<unknown>>(CATEGORY_URL, {
    method: "GET",
    headers: headers(),
  });

  if (!Array.isArray(response.data)) {
    return [];
  }

  return response.data
    .map(normalizeCategory)
    .filter((item): item is CategoryRow => item !== null);
}

/**
 * Exact API confirmed from Postman:
 * GET /api/v1/working-group-issues/categories/:id
 */
export async function getMasterDataCategoryById(
  id: number,
): Promise<CategoryRow> {
  const response = await apiRequest<ApiEnvelope<unknown>>(
    `${CATEGORY_URL}/${id}`,
    {
      method: "GET",
      headers: headers(),
    },
  );

  const category = normalizeCategory(response.data);

  if (!category) {
    throw new Error("Invalid category response.");
  }

  return category;
}

export async function createMasterDataCategory(
  value: MasterDataFormValue,
): Promise<void> {
  await apiRequest(`${CATEGORY_URL}`, {
    method: "POST",
    headers: headers(true),
    body: JSON.stringify({
      name: value.name.trim(),
    }),
  });
}

export async function updateMasterDataCategory(
  id: number,
  value: MasterDataFormValue,
): Promise<void> {
  await apiRequest(`${CATEGORY_URL}/${id}`, {
    method: "PATCH",
    headers: headers(true),
    body: JSON.stringify({
      name: value.name.trim(),
    }),
  });
}

export async function deleteMasterDataCategory(
  id: number,
): Promise<void> {
  await apiRequest(`${CATEGORY_URL}/${id}`, {
    method: "DELETE",
    headers: headers(),
  });
}

export async function getMasterDataIndicators(): Promise<IndicatorRow[]> {
  const response = await apiRequest<unknown>(INDICATOR_URL, {
    method: "GET",
    headers: headers(),
  });

  const root = asRecord(response);
  const level1 = root ? root.data : null;
  const level1Record = asRecord(level1);
  const level2 = level1Record ? level1Record.data : null;
  const level2Record = asRecord(level2);

  const nestedItems = level2Record?.items;
  const directItems = level1Record?.items;

  const items = Array.isArray(nestedItems)
    ? nestedItems
    : Array.isArray(directItems)
      ? directItems
      : Array.isArray(level1)
        ? level1
        : [];

  return items
    .map(normalizeIndicator)
    .filter((item): item is IndicatorRow => item !== null);
}

export async function createMasterDataIndicator(
  value: MasterDataFormValue,
): Promise<void> {
  await apiRequest(INDICATOR_URL, {
    method: "POST",
    headers: headers(true),
    body: JSON.stringify({
      name: value.name.trim(),
      description: value.description.trim() || null,
    }),
  });
}

// Keep these if your backend uses the conventional /indicators/:id routes.
export async function updateMasterDataIndicator(
  id: number,
  value: MasterDataFormValue,
): Promise<void> {
  await apiRequest(`${INDICATOR_URL}/${id}`, {
    method: "PATCH",
    headers: headers(true),
    body: JSON.stringify({
      name: value.name.trim(),
      description: value.description.trim() || null,
    }),
  });
}

export async function deleteMasterDataIndicator(
  id: number,
): Promise<void> {
  await apiRequest(`${INDICATOR_URL}/${id}`, {
    method: "DELETE",
    headers: headers(),
  });
}

export async function getMasterDataStatuses(): Promise<StatusRow[]> {
  const response = await apiRequest<unknown>(STATUS_URL, {
    method: "GET",
    headers: headers(),
  });

  const root = asRecord(response);
  const rootData = root?.data;

  let items: unknown[] = [];

  if (Array.isArray(rootData)) {
    items = rootData;
  } else {
    const rootDataRecord = asRecord(rootData);

    if (Array.isArray(rootDataRecord?.items)) {
      items = rootDataRecord.items;
    } else {
      const nested = asRecord(rootDataRecord?.data);

      if (Array.isArray(nested?.items)) {
        items = nested.items;
      }
    }
  }

  return items
    .map(normalizeStatus)
    .filter((item): item is StatusRow => item !== null);
}
