import { createCachedLoader } from "@/services/dropdownCache";
import { getAuthToken } from "@/utils/token";
import {
  NONE_SELECT_VALUE,
  type AlleleCallQuery,
  type AlleleCallRecord,
  type CallSetDetail,
  type CallSetQuery,
  type CallSetRecord,
  type SelectOption,
} from "./types";

interface BrapiPagination {
  currentPage: number;
  pageSize: number;
  totalCount: number;
  totalPages: number;
}

interface BrapiListResponse<T> {
  metadata: {
    pagination: BrapiPagination;
    status: Array<Record<string, unknown>>;
    datafiles: Array<Record<string, unknown>>;
  };
  result: {
    data: T[];
  };
}

interface BrapiSingleResponse<T> {
  metadata: {
    pagination: BrapiPagination;
    status: Array<Record<string, unknown>>;
    datafiles: Array<Record<string, unknown>>;
  };
  result: T;
}

interface SampleResponse {
  sampleDbId: string;
  sampleName: string | null;
}

interface VariantSetLookup {
  value: string;
  label: string;
}

type CallSetPayload = Partial<Record<
  "id" | "call_set_name" | "sample_id" | "variant_set_ids",
  unknown
>>;

type AlleleCallPayload = Partial<Record<
  "call_set_id" | "variant_id" | "genotype" | "read_depth" | "phase_set",
  unknown
>>;

const apiBase = () => {
  if (typeof window !== "undefined") return "";
  return process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL || "http://localhost:8000";
};

async function request<T>(path: string, init?: RequestInit): Promise<T> {
  const token = getAuthToken();
  const response = await fetch(`${apiBase()}${path}`, {
    ...init,
    headers: {
      "Content-Type": "application/json",
      ...(token ? { Authorization: `Bearer ${token}` } : {}),
      ...(init?.headers || {}),
    },
  });

  if (!response.ok) {
    const detail = await response.text();
    throw new Error(detail || `Request failed: ${response.status}`);
  }
  return response.json() as Promise<T>;
}

const optionalText = (value: unknown) => {
  const normalized = String(value ?? "").trim();
  if (!normalized || normalized === NONE_SELECT_VALUE) return null;
  return normalized;
};

const requiredText = (value: unknown, message: string) => {
  const normalized = optionalText(value);
  if (!normalized) throw new Error(message);
  return normalized;
};

const normalizeVariantSetIds = (value: unknown): string[] => {
  if (!Array.isArray(value)) return [];
  return value
    .map((item) => String(item ?? "").trim())
    .filter(Boolean);
};

const sampleLoader = createCachedLoader(async () => {
  const response = await request<BrapiListResponse<SampleResponse>>("/brapi/v2/samples?page=0&pageSize=10");
  return response.result.data.map((item) => ({
    value: item.sampleDbId,
    label: item.sampleName || item.sampleDbId,
  }));
});

const variantSetLoader = createCachedLoader(async () => {
  const response = await request<BrapiListResponse<{ variantSetDbId: string; variantSetName: string | null }>>(
    "/brapi/v2/variantsets?page=0&pageSize=10",
  );
  return response.result.data.map((item) => ({
    value: item.variantSetDbId,
    label: item.variantSetName || item.variantSetDbId,
  }));
});

export function invalidateCallSetPageCache() {
  sampleLoader.invalidate();
  variantSetLoader.invalidate();
}

const mapCallSet = (
  item: CallSetRecord,
  samples: SelectOption[],
  variantSets: VariantSetLookup[],
): CallSetRecord => {
  const sampleDbId = item.sampleDbId || item.sample_id || null;
  const sample = samples.find((entry) => entry.value === sampleDbId);
  const variantSetIds = normalizeVariantSetIds(item.variantSetDbIds ?? item.variant_set_ids);
  const variantSetNames = variantSetIds
    .map((id) => variantSets.find((entry) => entry.value === id)?.label || id)
    .join("、");

  return {
    ...item,
    id: item.callSetDbId || item.id,
    call_set_name: item.call_set_name || item.callSetName || null,
    sample_id: sampleDbId,
    sample_name: item.sample_name || item.sampleName || sample?.label || null,
    variant_set_ids: variantSetIds,
    variant_set_names: variantSetNames || "—",
  };
};

const filterCallSetRows = (
  rows: CallSetRecord[],
  query: CallSetQuery | undefined,
  samples: SelectOption[],
  variantSets: VariantSetLookup[],
): CallSetRecord[] => {
  const nameFilter = String(query?.call_set_name ?? "").trim().toLowerCase();
  const sampleId = optionalText(query?.sample_id);
  const variantSetId = optionalText(query?.variant_set_id);

  return rows
    .map((item) => mapCallSet(item, samples, variantSets))
    .filter((item) => {
      if (nameFilter && !String(item.call_set_name ?? "").toLowerCase().includes(nameFilter)) return false;
      if (sampleId && item.sample_id !== sampleId) return false;
      if (variantSetId && !(item.variant_set_ids ?? []).includes(variantSetId)) return false;
      return true;
    });
};

const callSetBody = (payload: CallSetPayload) => ({
  callSetName: requiredText(payload.call_set_name, "CallSet 名称不能为空"),
  sampleDbId: requiredText(payload.sample_id, "Sample 不能为空"),
  variantSetDbIds: normalizeVariantSetIds(payload.variant_set_ids),
});

export const normalizeCallSetFormData = (row: CallSetRecord) => ({
  id: row.id,
  call_set_name: row.call_set_name || row.callSetName || "",
  sample_id: row.sample_id && row.sample_id !== NONE_SELECT_VALUE ? row.sample_id : NONE_SELECT_VALUE,
  variant_set_ids: normalizeVariantSetIds(row.variant_set_ids ?? row.variantSetDbIds),
});

export async function loadCallSetPageData(params: {
  query?: CallSetQuery;
  force?: boolean;
} = {}): Promise<{
  options: { samples: SelectOption[]; variantSets: SelectOption[] };
  rows: CallSetRecord[];
}> {
  const force = params.force ?? false;
  const [samples, variantSets] = await Promise.all([
    sampleLoader.load(force),
    variantSetLoader.load(force),
  ]);

  const searchParams = new URLSearchParams({ page: "0", pageSize: "10" });
  const sampleId = optionalText(params.query?.sample_id);
  const variantSetId = optionalText(params.query?.variant_set_id);
  if (sampleId) searchParams.set("sampleDbId", sampleId);
  if (variantSetId) searchParams.set("variantSetDbId", variantSetId);

  const response = await request<BrapiListResponse<CallSetRecord>>(
    `/brapi/v2/callsets?${searchParams.toString()}`,
  );

  return {
    options: { samples, variantSets },
    rows: filterCallSetRows(response.result.data, params.query, samples, variantSets),
  };
}

export async function fetchCallSetDetail(callSetDbId: string): Promise<CallSetDetail> {
  const [detail, samples, variantSets] = await Promise.all([
    request<BrapiSingleResponse<CallSetRecord>>(`/brapi/v2/callsets/${encodeURIComponent(callSetDbId)}`),
    sampleLoader.load(),
    variantSetLoader.load(),
  ]);
  return mapCallSet(detail.result, samples, variantSets) as CallSetDetail;
}

export async function createCallSetRow(payload: CallSetPayload): Promise<CallSetRecord> {
  const response = await request<BrapiListResponse<CallSetRecord>>("/brapi/v2/callsets", {
    method: "POST",
    body: JSON.stringify({
      callSetDbId: optionalText(payload.id),
      ...callSetBody(payload),
    }),
  });
  invalidateCallSetPageCache();
  const [samples, variantSets] = await Promise.all([
    sampleLoader.load(true),
    variantSetLoader.load(true),
  ]);
  return mapCallSet(response.result.data[0], samples, variantSets);
}

export async function updateCallSetRow(id: string, payload: CallSetPayload): Promise<CallSetRecord> {
  const requestedId = optionalText(payload.id);
  if (requestedId && requestedId !== id) {
    throw new Error("CallSet ID 不可修改，请新建记录");
  }
  const response = await request<BrapiSingleResponse<CallSetRecord>>(
    `/brapi/v2/callsets/${encodeURIComponent(id)}`,
    {
      method: "PUT",
      body: JSON.stringify(callSetBody(payload)),
    },
  );
  invalidateCallSetPageCache();
  const [samples, variantSets] = await Promise.all([
    sampleLoader.load(true),
    variantSetLoader.load(true),
  ]);
  return mapCallSet(response.result, samples, variantSets);
}

export async function deleteCallSetRow(id: string): Promise<void> {
  await request(`/brapi/v2/callsets/${encodeURIComponent(id)}`, { method: "DELETE" });
  invalidateCallSetPageCache();
}

const mapAlleleCall = (item: AlleleCallRecord & { additionalInfo?: Record<string, unknown> }): AlleleCallRecord => ({
  ...item,
  id: String(item.additionalInfo?.callDbId ?? item.callDbId ?? item.id ?? ""),
  call_set_id: item.callSetDbId || item.call_set_id || null,
  call_set_name: item.callSetName || item.call_set_name || null,
  variant_id: item.variantDbId || item.variant_id || null,
  variant_name: item.variantName || item.variant_name || null,
  variant_set_id: item.variantSetDbId || item.variant_set_id || null,
  genotype: item.genotypeValue || item.genotype || null,
});

const filterAlleleCallRows = (rows: AlleleCallRecord[], query?: AlleleCallQuery) => {
  const callSetId = optionalText(query?.call_set_id);
  const variantId = optionalText(query?.variant_id);
  const variantSetId = optionalText(query?.variant_set_id);

  return rows
    .map(mapAlleleCall)
    .filter((item) => {
      if (callSetId && item.call_set_id !== callSetId) return false;
      if (variantId && item.variant_id !== variantId) return false;
      if (variantSetId && item.variant_set_id !== variantSetId) return false;
      return true;
    });
};

export async function loadAlleleCallRows(query?: AlleleCallQuery): Promise<AlleleCallRecord[]> {
  const searchParams = new URLSearchParams({ pageSize: "10" });
  const callSetId = optionalText(query?.call_set_id);
  const variantId = optionalText(query?.variant_id);
  const variantSetId = optionalText(query?.variant_set_id);
  if (callSetId) searchParams.set("callSetDbId", callSetId);
  if (variantId) searchParams.set("variantDbId", variantId);
  if (variantSetId) searchParams.set("variantSetDbId", variantSetId);

  const response = await request<BrapiListResponse<AlleleCallRecord>>(
    `/brapi/v2/calls?${searchParams.toString()}`,
  );
  return filterAlleleCallRows(response.result.data, query);
}

export async function importAlleleCallRows(payloads: AlleleCallPayload[]): Promise<AlleleCallRecord[]> {
  const body = payloads.map((payload) => ({
    callDbId: optionalText((payload as Record<string, unknown>).id),
    callSetDbId: requiredText(payload.call_set_id, "CallSet 不能为空"),
    variantDbId: requiredText(payload.variant_id, "Variant 不能为空"),
    genotype: requiredText(payload.genotype, "Genotype 不能为空"),
    readDepth: payload.read_depth === null || payload.read_depth === undefined || payload.read_depth === ""
      ? null
      : Number(payload.read_depth),
    phaseSet: optionalText(payload.phase_set),
  }));

  const response = await request<BrapiListResponse<AlleleCallRecord>>("/brapi/v2/calls/import", {
    method: "POST",
    body: JSON.stringify(body),
  });
  return response.result.data.map(mapAlleleCall);
}
