import { getAuthToken } from "@/utils/token";
import type { OntologyOption, TraitMethodScaleKind, TraitMethodScaleRecord } from "./types";
import { NONE_ONTOLOGY_VALUE } 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 OntologyResponse {
  ontologyDbId: string;
  ontologyName: string | null;
  ontology_name: string | null;
  version: string | null;
}

interface TraitResponse {
  traitDbId: string;
  traitName: string | null;
  traitClass: string | null;
  traitDescription: string | null;
  traitPUI: string | null;
  ontologyDbId: string | null;
  ontologyName: string | null;
}

interface MethodResponse {
  methodDbId: string;
  methodName: string | null;
  name: string | null;
  methodClass: string | null;
  description: string | null;
  formula: string | null;
  reference: string | null;
  methodPUI: string | null;
  ontologyDbId: string | null;
  ontologyName: string | null;
}

interface ScaleResponse {
  scaleDbId: string;
  scaleName: string | null;
  dataType: number | null;
  decimalPlaces: number | null;
  units: string | null;
  scalePUI: string | null;
  validValueMin: string | null;
  validValueMax: string | null;
  ontologyDbId: string | null;
  ontologyName: string | null;
}

type TraitMethodScalePayload = Partial<Record<
  "kind" | "db_id" | "name" | "ontology_id" | "description" | "units" | "class" | "pui" | "formula" | "reference" | "data_type" | "decimal_places" | "valid_value_min" | "valid_value_max",
  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}`);
  }
  return response.json() as Promise<T>;
}

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

const optionalNumber = (value: unknown) => {
  const normalized = optionalText(value);
  if (normalized === null) return null;
  const parsed = Number(normalized);
  return Number.isNaN(parsed) ? null : parsed;
};

const compositeId = (kind: TraitMethodScaleKind, dbId: string) => `${kind}:${dbId}`;

const parseCompositeId = (id: string): { kind: TraitMethodScaleKind; dbId: string } => {
  const [kind, ...rest] = id.split(":");
  if (kind !== "Trait" && kind !== "Method" && kind !== "Scale") {
    throw new Error("标准项类型无");
  }
  return { kind, dbId: rest.join(":") };
};

const mapTrait = (trait: TraitResponse): TraitMethodScaleRecord => ({
  id: compositeId("Trait", trait.traitDbId),
  db_id: trait.traitDbId,
  kind: "Trait",
  name: trait.traitName,
  ontology_id: trait.ontologyDbId,
  ontology_name: trait.ontologyName,
  description: trait.traitDescription,
  units: null,
  class: trait.traitClass,
  pui: trait.traitPUI,
  formula: null,
  reference: null,
  data_type: null,
  decimal_places: null,
  valid_value_min: null,
  valid_value_max: null,
});

const mapMethod = (method: MethodResponse): TraitMethodScaleRecord => ({
  id: compositeId("Method", method.methodDbId),
  db_id: method.methodDbId,
  kind: "Method",
  name: method.methodName || method.name,
  ontology_id: method.ontologyDbId,
  ontology_name: method.ontologyName,
  description: method.description,
  units: null,
  class: method.methodClass,
  pui: method.methodPUI,
  formula: method.formula,
  reference: method.reference,
  data_type: null,
  decimal_places: null,
  valid_value_min: null,
  valid_value_max: null,
});

const mapScale = (scale: ScaleResponse): TraitMethodScaleRecord => ({
  id: compositeId("Scale", scale.scaleDbId),
  db_id: scale.scaleDbId,
  kind: "Scale",
  name: scale.scaleName,
  ontology_id: scale.ontologyDbId,
  ontology_name: scale.ontologyName,
  description: null,
  units: scale.units,
  class: null,
  pui: scale.scalePUI,
  formula: null,
  reference: null,
  data_type: scale.dataType,
  decimal_places: scale.decimalPlaces,
  valid_value_min: scale.validValueMin,
  valid_value_max: scale.validValueMax,
});

const payloadKind = (payload: TraitMethodScalePayload): TraitMethodScaleKind => {
  const kind = String(payload.kind ?? "");
  if (kind === "Trait" || kind === "Method" || kind === "Scale") return kind;
  throw new Error("请选择类型");
};

const commonName = (payload: TraitMethodScalePayload) => {
  const name = optionalText(payload.name);
  if (!name) throw new Error("?????");
  return name;
};

export async function fetchOntologyOptions(): Promise<OntologyOption[]> {
  const response = await request<BrapiListResponse<OntologyResponse>>("/brapi/v2/ontologies?page=0&pageSize=10");
  return response.result.data.map((ontology) => ({
    value: ontology.ontologyDbId,
    label: `${ontology.ontologyName || ontology.ontology_name || ontology.ontologyDbId}${ontology.version ? ` / ${ontology.version}` : ""}`,
  }));
}

export async function fetchTraitMethodScaleRows(kind?: TraitMethodScaleKind): Promise<TraitMethodScaleRecord[]> {
  if (kind === "Trait") {
    const response = await request<BrapiListResponse<TraitResponse>>("/brapi/v2/traits?page=0&pageSize=10");
    return response.result.data.map(mapTrait);
  }

  if (kind === "Method") {
    const response = await request<BrapiListResponse<MethodResponse>>("/brapi/v2/methods?page=0&pageSize=10");
    return response.result.data.map(mapMethod);
  }

  if (kind === "Scale") {
    const response = await request<BrapiListResponse<ScaleResponse>>("/brapi/v2/scales?page=0&pageSize=10");
    return response.result.data.map(mapScale);
  }

  const [traits, methods, scales] = await Promise.all([
    request<BrapiListResponse<TraitResponse>>("/brapi/v2/traits?page=0&pageSize=10"),
    request<BrapiListResponse<MethodResponse>>("/brapi/v2/methods?page=0&pageSize=10"),
    request<BrapiListResponse<ScaleResponse>>("/brapi/v2/scales?page=0&pageSize=10"),
  ]);

  return [
    ...traits.result.data.map(mapTrait),
    ...methods.result.data.map(mapMethod),
    ...scales.result.data.map(mapScale),
  ].sort((left, right) => `${left.kind}-${left.name || left.db_id}`.localeCompare(`${right.kind}-${right.name || right.db_id}`));
}

export async function createTraitMethodScaleRow(payload: TraitMethodScalePayload): Promise<TraitMethodScaleRecord> {
  const kind = payloadKind(payload);
  const name = commonName(payload);
  const ontologyDbId = optionalText(payload.ontology_id);

  if (kind === "Trait") {
    const response = await request<BrapiListResponse<TraitResponse>>("/brapi/v2/traits", {
      method: "POST",
      body: JSON.stringify([{
        traitName: name,
        traitClass: optionalText(payload.class),
        traitDescription: optionalText(payload.description),
        traitPUI: optionalText(payload.pui),
        ontologyDbId,
      }]),
    });
    return mapTrait(response.result.data[0]);
  }

  if (kind === "Method") {
    const response = await request<BrapiListResponse<MethodResponse>>("/brapi/v2/methods", {
      method: "POST",
      body: JSON.stringify([{
        methodName: name,
        methodClass: optionalText(payload.class),
        description: optionalText(payload.description),
        formula: optionalText(payload.formula),
        reference: optionalText(payload.reference),
        methodPUI: optionalText(payload.pui),
        ontologyDbId,
      }]),
    });
    return mapMethod(response.result.data[0]);
  }

  const response = await request<BrapiListResponse<ScaleResponse>>("/brapi/v2/scales", {
    method: "POST",
    body: JSON.stringify([{
      scaleName: name,
      units: optionalText(payload.units),
      dataType: optionalNumber(payload.data_type),
      decimalPlaces: optionalNumber(payload.decimal_places),
      scalePUI: optionalText(payload.pui),
      validValueMin: optionalText(payload.valid_value_min),
      validValueMax: optionalText(payload.valid_value_max),
      ontologyDbId,
    }]),
  });
  return mapScale(response.result.data[0]);
}

export async function updateTraitMethodScaleRow(id: string, payload: TraitMethodScalePayload): Promise<TraitMethodScaleRecord> {
  const { kind, dbId } = parseCompositeId(id);
  if (payload.kind && payload.kind !== kind) {
    throw new Error("编辑时不能修改类型，请删除后重新新增");
  }
  const name = commonName(payload);
  const ontologyDbId = optionalText(payload.ontology_id);

  if (kind === "Trait") {
    const response = await request<BrapiSingleResponse<TraitResponse>>(`/brapi/v2/traits/${encodeURIComponent(dbId)}`, {
      method: "PUT",
      body: JSON.stringify({
        traitName: name,
        traitClass: optionalText(payload.class),
        traitDescription: optionalText(payload.description),
        traitPUI: optionalText(payload.pui),
        ontologyDbId,
      }),
    });
    return mapTrait(response.result);
  }

  if (kind === "Method") {
    const response = await request<BrapiSingleResponse<MethodResponse>>(`/brapi/v2/methods/${encodeURIComponent(dbId)}`, {
      method: "PUT",
      body: JSON.stringify({
        methodName: name,
        methodClass: optionalText(payload.class),
        description: optionalText(payload.description),
        formula: optionalText(payload.formula),
        reference: optionalText(payload.reference),
        methodPUI: optionalText(payload.pui),
        ontologyDbId,
      }),
    });
    return mapMethod(response.result);
  }

  const response = await request<BrapiSingleResponse<ScaleResponse>>(`/brapi/v2/scales/${encodeURIComponent(dbId)}`, {
    method: "PUT",
    body: JSON.stringify({
      scaleName: name,
      units: optionalText(payload.units),
      dataType: optionalNumber(payload.data_type),
      decimalPlaces: optionalNumber(payload.decimal_places),
      scalePUI: optionalText(payload.pui),
      validValueMin: optionalText(payload.valid_value_min),
      validValueMax: optionalText(payload.valid_value_max),
      ontologyDbId,
    }),
  });
  return mapScale(response.result);
}

export async function deleteTraitMethodScaleRow(id: string): Promise<void> {
  const { kind, dbId } = parseCompositeId(id);
  const path = kind === "Trait" ? "traits" : kind === "Method" ? "methods" : "scales";
  await request<BrapiSingleResponse<TraitResponse | MethodResponse | ScaleResponse>>(`/brapi/v2/${path}/${encodeURIComponent(dbId)}`, {
    method: "DELETE",
  });
}
