import { getAuthToken } from "@/utils/token";
import type {
  GermplasmDonorRecord,
  GermplasmInstituteRecord,
  GermplasmOriginRecord,
  GermplasmSynonymRecord,
  GermplasmTaxonRecord,
} from "./profileTypes";
import { fetchGermplasmDetail } from "./api";

interface BrapiSingleResponse<T> {
  result: T;
}

interface BrapiListResult<T> {
  result: { data: T[] };
}

type RawGermplasmDetail = Record<string, unknown> & {
  donors?: Array<Record<string, unknown>>;
  germplasmOrigin?: Array<Record<string, unknown>>;
  synonyms?: Array<Record<string, unknown>>;
  taxonIds?: Array<Record<string, 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 || `请求失败：${response.status}`);
  }
  if (response.status === 204) return undefined as T;
  return response.json() as Promise<T>;
}

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

const rowId = (prefix: string, index: number) => `${prefix}-${index}`;

const parseIndex = (id: string, prefix: string) => {
  if (!id.startsWith(`${prefix}-`)) return -1;
  return Number(id.slice(prefix.length + 1));
};

const readCoordinates = (item: Record<string, unknown>) => {
  const coordinates = item.coordinates as Record<string, unknown> | undefined;
  const geometry = coordinates?.geometry as Record<string, unknown> | undefined;
  const coords = geometry?.coordinates as number[] | undefined;
  if (!Array.isArray(coords) || coords.length < 2) {
    return { longitude: null, latitude: null };
  }
  return { longitude: coords[0], latitude: coords[1] };
};

const buildPointCoordinates = (longitude: unknown, latitude: unknown) => {
  const lon = Number(longitude);
  const lat = Number(latitude);
  if (Number.isNaN(lon) || Number.isNaN(lat)) return null;
  return {
    type: "Feature",
    geometry: {
      type: "Point",
      coordinates: [lon, lat],
    },
  };
};

export async function fetchGermplasmProfileDetail(germplasmDbId: string): Promise<RawGermplasmDetail> {
  return fetchGermplasmDetail(germplasmDbId) as unknown as RawGermplasmDetail;
}

export async function fetchDonorRows(germplasmDbId: string): Promise<GermplasmDonorRecord[]> {
  const detail = await fetchGermplasmProfileDetail(germplasmDbId);
  return (detail.donors ?? []).map((item, index) => ({
    id: rowId("donor", index),
    donor_accession_number: optionalText(item.donorAccessionNumber),
    donor_institute_code: optionalText(item.donorInstituteCode),
    germplasmpui: optionalText(item.germplasmPUI),
  }));
}

export async function fetchOriginRows(germplasmDbId: string): Promise<GermplasmOriginRecord[]> {
  const detail = await fetchGermplasmProfileDetail(germplasmDbId);
  return (detail.germplasmOrigin ?? []).map((item, index) => {
    const { longitude, latitude } = readCoordinates(item);
    return {
      id: rowId("origin", index),
      coordinate_uncertainty: optionalText(item.coordinateUncertainty),
      longitude,
      latitude,
    };
  });
}

export async function fetchSynonymRows(germplasmDbId: string): Promise<GermplasmSynonymRecord[]> {
  const detail = await fetchGermplasmProfileDetail(germplasmDbId);
  return (detail.synonyms ?? []).map((item, index) => ({
    id: rowId("synonym", index),
    synonym: optionalText(item.synonym),
    type: optionalText(item.type),
  }));
}

export async function fetchTaxonRows(germplasmDbId: string): Promise<GermplasmTaxonRecord[]> {
  const detail = await fetchGermplasmProfileDetail(germplasmDbId);
  return (detail.taxonIds ?? []).map((item, index) => ({
    id: rowId("taxon", index),
    source_name: optionalText(item.sourceName),
    taxon_id: optionalText(item.taxonId),
  }));
}

const mapInstituteRow = (item: Record<string, unknown>): GermplasmInstituteRecord => {
  const instituteDbId = String(item.instituteDbId ?? item.institute_db_id ?? item.id ?? "");
  return {
    id: instituteDbId,
    institute_db_id: instituteDbId,
    institute_type: optionalText(item.instituteType ?? item.institute_type),
    institute_code: optionalText(item.instituteCode ?? item.institute_code),
    institute_name: optionalText(item.instituteName ?? item.institute_name),
    institute_address: optionalText(item.instituteAddress ?? item.institute_address),
  };
};

export async function fetchInstituteRows(germplasmDbId: string): Promise<GermplasmInstituteRecord[]> {
  const response = await request<BrapiListResult<Record<string, unknown>>>(
    `/brapi/v2/germplasm/${encodeURIComponent(germplasmDbId)}/institutes`,
  );
  return response.result.data.map(mapInstituteRow);
}

async function saveDonorRows(germplasmDbId: string, rows: GermplasmDonorRecord[]) {
  await request(`/brapi/v2/germplasm/${encodeURIComponent(germplasmDbId)}`, {
    method: "PUT",
    body: JSON.stringify({
      donors: rows.map((row) => ({
        donorAccessionNumber: optionalText(row.donor_accession_number),
        donorInstituteCode: optionalText(row.donor_institute_code),
        germplasmPUI: optionalText(row.germplasmpui),
      })),
    }),
  });
}

async function saveOriginRows(germplasmDbId: string, rows: GermplasmOriginRecord[]) {
  await request(`/brapi/v2/germplasm/${encodeURIComponent(germplasmDbId)}`, {
    method: "PUT",
    body: JSON.stringify({
      germplasmOrigin: rows.map((row) => ({
        coordinateUncertainty: optionalText(row.coordinate_uncertainty),
        coordinates: buildPointCoordinates(row.longitude, row.latitude),
      })),
    }),
  });
}

async function saveSynonymRows(germplasmDbId: string, rows: GermplasmSynonymRecord[]) {
  await request(`/brapi/v2/germplasm/${encodeURIComponent(germplasmDbId)}`, {
    method: "PUT",
    body: JSON.stringify({
      synonyms: rows.map((row) => ({
        synonym: requiredSynonym(row.synonym),
        type: optionalText(row.type),
      })),
    }),
  });
}

async function saveTaxonRows(germplasmDbId: string, rows: GermplasmTaxonRecord[]) {
  await request(`/brapi/v2/germplasm/${encodeURIComponent(germplasmDbId)}`, {
    method: "PUT",
    body: JSON.stringify({
      taxonIds: rows.map((row) => ({
        sourceName: optionalText(row.source_name),
        taxonId: requiredTaxonId(row.taxon_id),
      })),
    }),
  });
}

const requiredSynonym = (value: unknown) => {
  const normalized = optionalText(value);
  if (!normalized) throw new Error("请填写别名");
  return normalized;
};

const requiredTaxonId = (value: unknown) => {
  const normalized = optionalText(value);
  if (!normalized) throw new Error("请填写 taxon ID");
  return normalized;
};

async function mutateListRows<T extends { id: string }>(
  germplasmDbId: string,
  loadRows: (id: string) => Promise<T[]>,
  saveRows: (id: string, rows: T[]) => Promise<void>,
  mutate: (rows: T[]) => T[],
) {
  const rows = await loadRows(germplasmDbId);
  await saveRows(germplasmDbId, mutate(rows));
}

export async function createDonorRow(germplasmDbId: string, payload: Record<string, unknown>) {
  await mutateListRows(germplasmDbId, fetchDonorRows, saveDonorRows, (rows) => [
    ...rows,
    {
      id: rowId("donor", rows.length),
      donor_accession_number: optionalText(payload.donor_accession_number),
      donor_institute_code: optionalText(payload.donor_institute_code),
      germplasmpui: optionalText(payload.germplasmpui),
    },
  ]);
  return fetchDonorRows(germplasmDbId);
}

export async function updateDonorRow(germplasmDbId: string, id: string, payload: Record<string, unknown>) {
  const index = parseIndex(id, "donor");
  await mutateListRows(germplasmDbId, fetchDonorRows, saveDonorRows, (rows) => rows.map((row, idx) => (
    idx === index
      ? {
        ...row,
        donor_accession_number: optionalText(payload.donor_accession_number),
        donor_institute_code: optionalText(payload.donor_institute_code),
        germplasmpui: optionalText(payload.germplasmpui),
      }
      : row
  )));
}

export async function deleteDonorRow(germplasmDbId: string, id: string) {
  const index = parseIndex(id, "donor");
  await mutateListRows(germplasmDbId, fetchDonorRows, saveDonorRows, (rows) => rows.filter((_, idx) => idx !== index));
}

export async function createOriginRow(germplasmDbId: string, payload: Record<string, unknown>) {
  await mutateListRows(germplasmDbId, fetchOriginRows, saveOriginRows, (rows) => [
    ...rows,
    {
      id: rowId("origin", rows.length),
      coordinate_uncertainty: optionalText(payload.coordinate_uncertainty),
      longitude: payload.longitude ?? "",
      latitude: payload.latitude ?? "",
    },
  ]);
}

export async function updateOriginRow(germplasmDbId: string, id: string, payload: Record<string, unknown>) {
  const index = parseIndex(id, "origin");
  await mutateListRows(germplasmDbId, fetchOriginRows, saveOriginRows, (rows) => rows.map((row, idx) => (
    idx === index
      ? {
        ...row,
        coordinate_uncertainty: optionalText(payload.coordinate_uncertainty),
        longitude: payload.longitude ?? "",
        latitude: payload.latitude ?? "",
      }
      : row
  )));
}

export async function deleteOriginRow(germplasmDbId: string, id: string) {
  const index = parseIndex(id, "origin");
  await mutateListRows(germplasmDbId, fetchOriginRows, saveOriginRows, (rows) => rows.filter((_, idx) => idx !== index));
}

export async function createSynonymRow(germplasmDbId: string, payload: Record<string, unknown>) {
  await mutateListRows(germplasmDbId, fetchSynonymRows, saveSynonymRows, (rows) => [
    ...rows,
    {
      id: rowId("synonym", rows.length),
      synonym: requiredSynonym(payload.synonym),
      type: optionalText(payload.type),
    },
  ]);
}

export async function updateSynonymRow(germplasmDbId: string, id: string, payload: Record<string, unknown>) {
  const index = parseIndex(id, "synonym");
  await mutateListRows(germplasmDbId, fetchSynonymRows, saveSynonymRows, (rows) => rows.map((row, idx) => (
    idx === index
      ? { ...row, synonym: requiredSynonym(payload.synonym), type: optionalText(payload.type) }
      : row
  )));
}

export async function deleteSynonymRow(germplasmDbId: string, id: string) {
  const index = parseIndex(id, "synonym");
  await mutateListRows(germplasmDbId, fetchSynonymRows, saveSynonymRows, (rows) => rows.filter((_, idx) => idx !== index));
}

export async function createTaxonRow(germplasmDbId: string, payload: Record<string, unknown>) {
  await mutateListRows(germplasmDbId, fetchTaxonRows, saveTaxonRows, (rows) => [
    ...rows,
    {
      id: rowId("taxon", rows.length),
      source_name: optionalText(payload.source_name),
      taxon_id: requiredTaxonId(payload.taxon_id),
    },
  ]);
}

export async function updateTaxonRow(germplasmDbId: string, id: string, payload: Record<string, unknown>) {
  const index = parseIndex(id, "taxon");
  await mutateListRows(germplasmDbId, fetchTaxonRows, saveTaxonRows, (rows) => rows.map((row, idx) => (
    idx === index
      ? { ...row, source_name: optionalText(payload.source_name), taxon_id: requiredTaxonId(payload.taxon_id) }
      : row
  )));
}

export async function deleteTaxonRow(germplasmDbId: string, id: string) {
  const index = parseIndex(id, "taxon");
  await mutateListRows(germplasmDbId, fetchTaxonRows, saveTaxonRows, (rows) => rows.filter((_, idx) => idx !== index));
}

const instituteBody = (payload: Record<string, unknown>) => ({
  instituteDbId: optionalText(payload.id),
  instituteType: requiredText(payload.institute_type, "请选择机构类型"),
  instituteCode: optionalText(payload.institute_code),
  instituteName: optionalText(payload.institute_name),
  instituteAddress: optionalText(payload.institute_address),
});

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

export async function createInstituteRow(germplasmDbId: string, payload: Record<string, unknown>) {
  const response = await request<BrapiListResult<Record<string, unknown>>>(
    `/brapi/v2/germplasm/${encodeURIComponent(germplasmDbId)}/institutes`,
    { method: "POST", body: JSON.stringify(instituteBody(payload)) },
  );
  return mapInstituteRow(response.result.data[0]);
}

export async function updateInstituteRow(germplasmDbId: string, id: string, payload: Record<string, unknown>) {
  const response = await request<BrapiListResult<Record<string, unknown>>>(
    `/brapi/v2/germplasm/${encodeURIComponent(germplasmDbId)}/institutes/${encodeURIComponent(id)}`,
    { method: "PUT", body: JSON.stringify(instituteBody(payload)) },
  );
  return mapInstituteRow(response.result.data[0]);
}

export async function deleteInstituteRow(germplasmDbId: string, id: string) {
  await request(
    `/brapi/v2/germplasm/${encodeURIComponent(germplasmDbId)}/institutes/${encodeURIComponent(id)}`,
    { method: "DELETE" },
  );
}

export async function fetchDonorRecord(germplasmDbId: string, id: string) {
  const rows = await fetchDonorRows(germplasmDbId);
  const row = rows.find((item) => item.id === id);
  if (!row) throw new Error("Donor 记录不存在");
  return row;
}

export async function fetchOriginRecord(germplasmDbId: string, id: string) {
  const rows = await fetchOriginRows(germplasmDbId);
  const row = rows.find((item) => item.id === id);
  if (!row) throw new Error("Origin 记录不存在");
  return row;
}

export async function fetchSynonymRecord(germplasmDbId: string, id: string) {
  const rows = await fetchSynonymRows(germplasmDbId);
  const row = rows.find((item) => item.id === id);
  if (!row) throw new Error("Synonym 记录不存在");
  return row;
}

export async function fetchTaxonRecord(germplasmDbId: string, id: string) {
  const rows = await fetchTaxonRows(germplasmDbId);
  const row = rows.find((item) => item.id === id);
  if (!row) throw new Error("Taxon 记录不存在");
  return row;
}

export async function fetchInstituteRecord(germplasmDbId: string, id: string) {
  const rows = await fetchInstituteRows(germplasmDbId);
  const row = rows.find((item) => item.id === id);
  if (!row) throw new Error("Institute 记录不存在");
  return row;
}
