import { createCachedLoader, loadStudyOptions, type SelectOption as CachedSelectOption } from "@/services/dropdownCache";
import { getAuthToken } from "@/utils/token";
import {
  NONE_SELECT_VALUE,
  type SelectOption,
  type VariantSetAnalysisItem,
  type VariantSetDetail,
  type VariantSetFormatItem,
  type VariantSetQuery,
  type VariantSetRecord,
} 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 ReferenceSetResponse {
  referenceSetDbId: string;
  referenceSetName: string | null;
}

interface StudyLookup {
  studyDbId: string;
  studyName: string | null;
}

type VariantSetPayload = Partial<Record<
  "id" | "variant_set_name" | "reference_set_id" | "study_id",
  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 mapVariantSet = (
  item: VariantSetRecord,
  referenceSets: ReferenceSetResponse[],
  studies: StudyLookup[],
): VariantSetRecord => {
  const referenceSetDbId = item.referenceSetDbId || item.reference_set_id || null;
  const studyDbId = item.studyDbId || item.study_id || null;
  const referenceSet = referenceSets.find((entry) => entry.referenceSetDbId === referenceSetDbId);
  const study = studies.find((entry) => entry.studyDbId === studyDbId);

  return {
    ...item,
    id: item.variantSetDbId || item.id,
    variant_set_name: item.variant_set_name || item.variantSetName || null,
    reference_set_id: referenceSetDbId,
    reference_set_name: item.reference_set_name || referenceSet?.referenceSetName || null,
    study_id: studyDbId,
    study_name: item.study_name || study?.studyName || null,
    variant_count: item.variant_count ?? item.variantCount ?? null,
    callset_count: item.callset_count ?? item.callSetCount ?? null,
    analysis_count: item.analysis_count ?? item.analysis?.length ?? null,
    format_count: item.format_count ?? item.availableFormats?.length ?? null,
  };
};

const variantSetBody = (payload: VariantSetPayload) => ({
  variantSetName: requiredText(payload.variant_set_name, "VariantSet 名称不能为空"),
  referenceSetDbId: optionalText(payload.reference_set_id),
  studyDbId: optionalText(payload.study_id),
});

export const normalizeVariantSetFormData = (row: VariantSetRecord) => ({
  id: row.id,
  variant_set_name: row.variant_set_name || row.variantSetName || "",
  reference_set_id: row.reference_set_id && row.reference_set_id !== NONE_SELECT_VALUE
    ? row.reference_set_id
    : NONE_SELECT_VALUE,
  study_id: row.study_id && row.study_id !== NONE_SELECT_VALUE ? row.study_id : NONE_SELECT_VALUE,
});

const referenceSetLoader = createCachedLoader(async () => {
  const response = await request<BrapiListResponse<ReferenceSetResponse>>("/brapi/v2/referencesets?page=0&pageSize=10");
  return response.result.data;
});

const variantSetListLoader = createCachedLoader(async () => {
  const response = await request<BrapiListResponse<VariantSetRecord>>("/brapi/v2/variantsets?page=0&pageSize=10");
  return response.result.data;
});

const toStudyLookup = (studies: CachedSelectOption[]): StudyLookup[] =>
  studies.map((item) => ({ studyDbId: item.value, studyName: item.label }));

const filterVariantSetRows = (
  rows: VariantSetRecord[],
  query: VariantSetQuery | undefined,
  referenceSets: ReferenceSetResponse[],
  studies: StudyLookup[],
): VariantSetRecord[] => {
  const nameFilter = String(query?.variant_set_name ?? "").trim().toLowerCase();
  const referenceSetId = optionalText(query?.reference_set_id);
  const studyId = optionalText(query?.study_id);

  return rows
    .map((item) => mapVariantSet(item, referenceSets, studies))
    .filter((item) => {
      if (nameFilter && !String(item.variant_set_name ?? "").toLowerCase().includes(nameFilter)) return false;
      if (referenceSetId && item.reference_set_id !== referenceSetId) return false;
      if (studyId && item.study_id !== studyId) return false;
      return true;
    });
};

export function invalidateVariantSetPageCache() {
  referenceSetLoader.invalidate();
  variantSetListLoader.invalidate();
}

export async function fetchVariantSetOptions(force = false): Promise<{
  referenceSets: SelectOption[];
  studies: SelectOption[];
}> {
  const [referenceSets, studies] = await Promise.all([
    referenceSetLoader.load(force),
    loadStudyOptions(force),
  ]);

  return {
    referenceSets: referenceSets.map((item) => ({
      value: item.referenceSetDbId,
      label: item.referenceSetName || item.referenceSetDbId,
    })),
    studies,
  };
}

export async function fetchVariantSetRows(query?: VariantSetQuery, force = false): Promise<VariantSetRecord[]> {
  const [referenceSets, studies, variantSets] = await Promise.all([
    referenceSetLoader.load(force),
    loadStudyOptions(force),
    variantSetListLoader.load(force),
  ]);
  return filterVariantSetRows(variantSets, query, referenceSets, toStudyLookup(studies));
}

export async function fetchVariantSetDetail(variantSetDbId: string): Promise<VariantSetDetail> {
  const [detail, options] = await Promise.all([
    request<BrapiSingleResponse<VariantSetDetail>>(`/brapi/v2/variantsets/${encodeURIComponent(variantSetDbId)}`),
    fetchVariantSetOptions(),
  ]);

  const referenceSet = options.referenceSets.find((item) => item.value === detail.result.referenceSetDbId);
  const study = options.studies.find((item) => item.value === detail.result.studyDbId);

  return {
    ...mapVariantSet(detail.result, [], []),
    id: detail.result.variantSetDbId || variantSetDbId,
    variant_set_name: detail.result.variantSetName || detail.result.variant_set_name || null,
    reference_set_id: detail.result.referenceSetDbId || null,
    reference_set_name: referenceSet?.label || null,
    study_id: detail.result.studyDbId || null,
    study_name: study?.label || null,
    variant_count: detail.result.variantCount ?? null,
    callset_count: detail.result.callSetCount ?? null,
    analysis: detail.result.analysis || [],
    availableFormats: detail.result.availableFormats || [],
    analysis_count: detail.result.analysis?.length ?? 0,
    format_count: detail.result.availableFormats?.length ?? 0,
  };
}

export async function fetchVariantSetVariants(variantSetDbId: string) {
  const response = await request<BrapiListResponse<Record<string, unknown>>>(
    `/brapi/v2/variantsets/${encodeURIComponent(variantSetDbId)}/variants?pageSize=10`,
  );
  return response.result.data;
}

export async function fetchVariantSetCallsets(variantSetDbId: string) {
  const response = await request<BrapiListResponse<Record<string, unknown>>>(
    `/brapi/v2/variantsets/${encodeURIComponent(variantSetDbId)}/callsets?page=0&pageSize=10`,
  );
  return response.result.data;
}

export async function createVariantSetRow(payload: VariantSetPayload): Promise<VariantSetRecord> {
  const response = await request<BrapiListResponse<VariantSetRecord>>("/brapi/v2/variantsets", {
    method: "POST",
    body: JSON.stringify({
      variantSetDbId: requiredText(payload.id, "VariantSet ID 不能为空"),
      ...variantSetBody(payload),
    }),
  });
  invalidateVariantSetPageCache();
  const [referenceSets, studies] = await Promise.all([
    referenceSetLoader.load(true),
    loadStudyOptions(true),
  ]);
  return mapVariantSet(response.result.data[0], referenceSets, toStudyLookup(studies));
}

export async function updateVariantSetRow(id: string, payload: VariantSetPayload): Promise<VariantSetRecord> {
  const requestedId = optionalText(payload.id);
  if (requestedId && requestedId !== id) {
    throw new Error("VariantSet ID 不可修改，请新建记录");
  }
  const response = await request<BrapiSingleResponse<VariantSetRecord>>(
    `/brapi/v2/variantsets/${encodeURIComponent(id)}`,
    {
      method: "PUT",
      body: JSON.stringify(variantSetBody(payload)),
    },
  );
  invalidateVariantSetPageCache();
  const [referenceSets, studies] = await Promise.all([
    referenceSetLoader.load(true),
    loadStudyOptions(true),
  ]);
  return mapVariantSet(response.result, referenceSets, toStudyLookup(studies));
}

export async function deleteVariantSetRow(id: string): Promise<void> {
  await request<BrapiSingleResponse<VariantSetRecord>>(`/brapi/v2/variantsets/${encodeURIComponent(id)}`, {
    method: "DELETE",
  });
  invalidateVariantSetPageCache();
}

const mapAnalysisRow = (item: VariantSetAnalysisItem): VariantSetAnalysisItem => ({
  ...item,
  id: item.analysisDbId || item.id,
  analysisName: item.analysisName ?? null,
  type: item.type ?? null,
  description: item.description ?? null,
  software: Array.isArray(item.software)
    ? item.software
    : (item.software ? [String(item.software)] : []),
  created: item.created ?? null,
  updated: item.updated ?? null,
});

const mapFormatRow = (item: VariantSetFormatItem): VariantSetFormatItem => ({
  ...item,
  id: item.formatDbId || item.id,
  dataFormat: item.dataFormat ?? item.data_format ?? null,
  fileFormat: item.fileFormat ?? item.file_format ?? null,
  fileURL: item.fileURL ?? item.fileurl ?? null,
  expandHomozygotes: item.expandHomozygotes ?? item.expand_homozygotes ?? null,
  sepPhased: item.sepPhased ?? item.sep_phased ?? null,
  sepUnphased: item.sepUnphased ?? item.sep_unphased ?? null,
  unknownString: item.unknownString ?? item.unknown_string ?? null,
});

const parseSoftwareLines = (value: unknown) => String(value ?? "")
  .split(/\r?\n/)
  .map((line) => line.trim())
  .filter(Boolean);

const isValidUrl = (value: string) => {
  try {
    const url = new URL(value);
    return url.protocol === "http:" || url.protocol === "https:";
  } catch {
    return false;
  }
};

const analysisBody = (payload: Partial<Record<string, unknown>>) => {
  const analysisName = requiredText(payload.analysis_name, "分析名称不能为空");
  const software = parseSoftwareLines(payload.software_text);
  for (const item of software) {
    if (item.startsWith("http") && !isValidUrl(item)) {
      throw new Error(`Software URL 格式无效：${item}`);
    }
  }
  return {
    analysisDbId: optionalText(payload.id),
    analysisName,
    description: optionalText(payload.description),
    type: optionalText(payload.type === NONE_SELECT_VALUE ? null : payload.type),
    created: optionalText(payload.created),
    updated: optionalText(payload.updated),
    software,
  };
};

const formatBody = (payload: Partial<Record<string, unknown>>) => {
  const dataFormat = requiredText(payload.data_format, "请选择 data_format");
  const fileFormat = requiredText(payload.file_format, "请选择 file_format");
  const fileURL = optionalText(payload.fileurl);
  if (fileURL && !isValidUrl(fileURL)) {
    throw new Error("fileurl 格式无效，请输入 http(s) URL");
  }
  return {
    formatDbId: optionalText(payload.id),
    dataFormat,
    fileFormat,
    fileURL,
    expandHomozygotes: payload.expand_homozygotes === true || payload.expand_homozygotes === "true",
    sepPhased: optionalText(payload.sep_phased),
    sepUnphased: optionalText(payload.sep_unphased),
    unknownString: optionalText(payload.unknown_string),
  };
};

export const normalizeVariantSetAnalysisFormData = (row: VariantSetAnalysisItem) => ({
  id: row.id || row.analysisDbId || "",
  analysis_name: row.analysisName || "",
  type: row.type ? row.type : NONE_SELECT_VALUE,
  description: row.description || "",
  software_text: (Array.isArray(row.software) ? row.software : []).join("\n"),
  created: row.created ? String(row.created).slice(0, 19) : "",
  updated: row.updated ? String(row.updated).slice(0, 19) : "",
});

export const normalizeVariantSetFormatFormData = (row: VariantSetFormatItem) => ({
  id: row.id || row.formatDbId || "",
  data_format: row.dataFormat || NONE_SELECT_VALUE,
  file_format: row.fileFormat || NONE_SELECT_VALUE,
  fileurl: row.fileURL || "",
  expand_homozygotes: row.expandHomozygotes === true,
  sep_phased: row.sepPhased || "",
  sep_unphased: row.sepUnphased || "",
  unknown_string: row.unknownString || "",
});

export async function fetchVariantSetAnalysisRows(variantSetDbId: string): Promise<VariantSetAnalysisItem[]> {
  const response = await request<BrapiListResponse<VariantSetAnalysisItem>>(
    `/brapi/v2/variantsets/${encodeURIComponent(variantSetDbId)}/analysis`,
  );
  return response.result.data.map(mapAnalysisRow);
}

export async function fetchVariantSetFormatRows(variantSetDbId: string): Promise<VariantSetFormatItem[]> {
  const response = await request<BrapiListResponse<VariantSetFormatItem>>(
    `/brapi/v2/variantsets/${encodeURIComponent(variantSetDbId)}/availableformats`,
  );
  return response.result.data.map(mapFormatRow);
}

export async function createVariantSetAnalysisRow(
  variantSetDbId: string,
  payload: Partial<Record<string, unknown>>,
): Promise<VariantSetAnalysisItem> {
  const response = await request<BrapiListResponse<VariantSetAnalysisItem>>(
    `/brapi/v2/variantsets/${encodeURIComponent(variantSetDbId)}/analysis`,
    { method: "POST", body: JSON.stringify(analysisBody(payload)) },
  );
  invalidateVariantSetPageCache();
  return mapAnalysisRow(response.result.data[0]);
}

export async function updateVariantSetAnalysisRow(
  variantSetDbId: string,
  id: string,
  payload: Partial<Record<string, unknown>>,
): Promise<VariantSetAnalysisItem> {
  const response = await request<BrapiListResponse<VariantSetAnalysisItem>>(
    `/brapi/v2/variantsets/${encodeURIComponent(variantSetDbId)}/analysis/${encodeURIComponent(id)}`,
    { method: "PUT", body: JSON.stringify(analysisBody(payload)) },
  );
  invalidateVariantSetPageCache();
  return mapAnalysisRow(response.result.data[0]);
}

export async function deleteVariantSetAnalysisRow(variantSetDbId: string, id: string): Promise<void> {
  await request(
    `/brapi/v2/variantsets/${encodeURIComponent(variantSetDbId)}/analysis/${encodeURIComponent(id)}`,
    { method: "DELETE" },
  );
  invalidateVariantSetPageCache();
}

export async function createVariantSetFormatRow(
  variantSetDbId: string,
  payload: Partial<Record<string, unknown>>,
): Promise<VariantSetFormatItem> {
  const response = await request<BrapiListResponse<VariantSetFormatItem>>(
    `/brapi/v2/variantsets/${encodeURIComponent(variantSetDbId)}/availableformats`,
    { method: "POST", body: JSON.stringify(formatBody(payload)) },
  );
  invalidateVariantSetPageCache();
  return mapFormatRow(response.result.data[0]);
}

export async function updateVariantSetFormatRow(
  variantSetDbId: string,
  id: string,
  payload: Partial<Record<string, unknown>>,
): Promise<VariantSetFormatItem> {
  const response = await request<BrapiListResponse<VariantSetFormatItem>>(
    `/brapi/v2/variantsets/${encodeURIComponent(variantSetDbId)}/availableformats/${encodeURIComponent(id)}`,
    { method: "PUT", body: JSON.stringify(formatBody(payload)) },
  );
  invalidateVariantSetPageCache();
  return mapFormatRow(response.result.data[0]);
}

export async function deleteVariantSetFormatRow(variantSetDbId: string, id: string): Promise<void> {
  await request(
    `/brapi/v2/variantsets/${encodeURIComponent(variantSetDbId)}/availableformats/${encodeURIComponent(id)}`,
    { method: "DELETE" },
  );
  invalidateVariantSetPageCache();
}

export async function loadVariantSetPageData(options?: { query?: VariantSetQuery; force?: boolean }) {
  const force = options?.force ?? false;
  const [referenceSets, studies, variantSets] = await Promise.all([
    referenceSetLoader.load(force),
    loadStudyOptions(force),
    variantSetListLoader.load(force),
  ]);

  return {
    options: {
      referenceSets: referenceSets.map((item) => ({
        value: item.referenceSetDbId,
        label: item.referenceSetName || item.referenceSetDbId,
      })),
      studies,
    },
    rows: filterVariantSetRows(variantSets, options?.query, referenceSets, toStudyLookup(studies)),
  };
}
