import type { Study } from "@/lib/api/types.gen";
import { toBrapiIsoDateTime } from "@/lib/brapiIso";
import {
  createCachedLoader,
  loadCommonCropNameOptions,
  loadLocationOptions,
  loadPersonOptions,
  loadSeasonOptions,
} from "@/services/dropdownCache";
import { getAuthToken } from "@/utils/token";
import {
  mapContactsFromApi,
  mapSeasonsFromApi,
  normalizeContactsForSave,
  normalizeSeasonsForSave,
  readStudyContacts,
  readStudySeasons,
} from "./components/studyRelationUtils";
import { NONE_SELECT_VALUE, type SelectOption, type StudyRecord, type TrialSelectOption } 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 CommonCropNamesResponse {
  result: {
    data: string[];
  };
}

interface TrialResponse {
  trialDbId: string;
  trialName: string | null;
  trial_name: string | null;
  programDbId: string | null;
  programName: string | null;
  program_name: string | null;
}

interface LocationResponse {
  locationDbId: string;
  locationName: string | null;
  abbreviation: string | null;
}

interface PersonResponse {
  personDbId: string;
  firstName?: string | null;
  lastName?: string | null;
  emailAddress?: string | null;
}

interface SeasonResponse {
  seasonDbId: string;
  seasonName: string | null;
  year: number | null;
}

type StudyPayload = Partial<Record<
  | "id"
  | "study_name"
  | "study_code"
  | "study_type"
  | "study_description"
  | "studypui"
  | "documentationurl"
  | "start_date"
  | "end_date"
  | "active"
  | "license"
  | "cultural_practices"
  | "observation_units_description"
  | "crop_id"
  | "location_id"
  | "program_id"
  | "trial_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 || `请求失败：${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 optionalBoolean = (value: unknown) => {
  const normalized = optionalText(value);
  if (normalized === null) return null;
  return normalized === "true" || normalized === "1" || normalized === "是" || normalized === "active";
};

const studyName = (payload: StudyPayload) => {
  const name = optionalText(payload.study_name);
  if (!name) throw new Error("请填写研究名");
  return name;
};

export const mapStudy = (study: Study): StudyRecord => {
  const commonCropName = study.commonCropName ?? null;
  const studyContacts = mapContactsFromApi(study.contacts);
  const studySeasons = mapSeasonsFromApi(study.seasons);

  return {
    id: study.studyDbId || "",
    studyDbId: study.studyDbId || "",
    studyName: study.studyName ?? null,
    study_name: study.studyName ?? null,
    studyCode: study.studyCode ?? null,
    study_code: study.studyCode ?? null,
    studyType: study.studyType ?? null,
    study_type: study.studyType ?? null,
    studyDescription: study.studyDescription ?? null,
    study_description: study.studyDescription ?? null,
    studyPUI: study.studyPUI ?? null,
    studypui: study.studyPUI ?? null,
    documentationURL: study.documentationURL ?? null,
    documentationurl: study.documentationURL ?? null,
    startDate: study.startDate ?? null,
    start_date: study.startDate ?? null,
    endDate: study.endDate ?? null,
    end_date: study.endDate ?? null,
    active: study.active ?? null,
    license: study.license ?? null,
    culturalPractices: study.culturalPractices ?? null,
    cultural_practices: study.culturalPractices ?? null,
    observationUnitsDescription: study.observationUnitsDescription ?? null,
    observation_units_description: study.observationUnitsDescription ?? null,
    cropDbId: null,
    crop_id: commonCropName,
    cropName: commonCropName,
    crop_name: commonCropName,
    locationDbId: study.locationDbId ?? null,
    location_id: study.locationDbId ?? null,
    locationName: study.locationName ?? null,
    location_name: study.locationName ?? null,
    programDbId: null,
    program_id: null,
    programName: null,
    program_name: null,
    trialDbId: study.trialDbId ?? null,
    trial_id: study.trialDbId ?? null,
    trialName: study.trialName ?? null,
    trial_name: study.trialName ?? null,
    contacts: studyContacts,
    seasons: study.seasons ?? [],
    studyContacts,
    studySeasons,
  };
};

export function enrichStudyRows(
  rows: StudyRecord[],
  trials: TrialSelectOption[],
): StudyRecord[] {
  const trialById = new Map(trials.map((trial) => [trial.value, trial]));

  return rows.map((row) => {
    const trial = row.trial_id ? trialById.get(row.trial_id) : undefined;
    if (!trial?.programDbId) return row;

    return {
      ...row,
      programDbId: trial.programDbId,
      program_id: trial.programDbId,
      programName: trial.programName ?? null,
      program_name: trial.programName ?? null,
    };
  });
}

const toRequestBody = (payload: StudyPayload & Record<string, unknown>) => {
  const trialDbId = optionalText(payload.trial_id);
  const locationDbId = optionalText(payload.location_id);
  const commonCropName = optionalText(payload.crop_id);

  return {
    studyName: studyName(payload),
    studyCode: optionalText(payload.study_code),
    studyType: optionalText(payload.study_type),
    studyDescription: optionalText(payload.study_description),
    studyPUI: optionalText(payload.studypui),
    documentationURL: optionalText(payload.documentationurl),
    startDate: toBrapiIsoDateTime(payload.start_date),
    endDate: toBrapiIsoDateTime(payload.end_date),
    active: optionalBoolean(payload.active),
    license: optionalText(payload.license),
    culturalPractices: optionalText(payload.cultural_practices),
    observationUnitsDescription: optionalText(payload.observation_units_description),
    contacts: normalizeContactsForSave(readStudyContacts(payload)),
    seasons: normalizeSeasonsForSave(readStudySeasons(payload)),
    ...(trialDbId ? { trialDbId } : {}),
    ...(locationDbId ? { locationDbId } : {}),
    ...(!trialDbId && commonCropName ? { commonCropName } : {}),
  };
};

export async function fetchStudyRows(): Promise<StudyRecord[]> {
  const response = await request<BrapiListResponse<Study>>("/brapi/v2/studies?page=0&pageSize=10");
  return response.result.data.map(mapStudy);
}

export async function fetchStudyDetail(studyDbId: string): Promise<StudyRecord> {
  const response = await request<BrapiSingleResponse<Study>>(`/brapi/v2/studies/${encodeURIComponent(studyDbId)}`);
  return mapStudy(response.result);
}

const studyTrialOptionsLoader = createCachedLoader(async () => {
  const response = await request<BrapiListResponse<TrialResponse>>("/brapi/v2/trials?page=0&pageSize=10");
  return response.result.data.map((trial) => ({
    value: trial.trialDbId,
    label: `${trial.trialName || trial.trial_name || trial.trialDbId}${trial.programName || trial.program_name ? ` / ${trial.programName || trial.program_name}` : ""}`,
    programDbId: trial.programDbId ?? null,
    programName: trial.programName || trial.program_name || null,
  }));
});

export async function fetchStudyOptions(force = false): Promise<{
  crops: SelectOption[];
  locations: SelectOption[];
  trials: TrialSelectOption[];
  persons: SelectOption[];
  seasons: SelectOption[];
}> {
  const [crops, locations, trials, persons, seasons] = await Promise.allSettled([
    loadCommonCropNameOptions(force),
    loadLocationOptions(force),
    studyTrialOptionsLoader.load(force),
    loadPersonOptions(force),
    loadSeasonOptions(force),
  ]);

  return {
    crops: crops.status === "fulfilled" ? crops.value : [],
    locations: (locations.status === "fulfilled" ? locations.value : []).filter((location) => location.value && location.label),
    trials: trials.status === "fulfilled" ? trials.value : [],
    persons: persons.status === "fulfilled" ? persons.value : [],
    seasons: seasons.status === "fulfilled" ? seasons.value : [],
  };
}

export async function createStudyRow(payload: StudyPayload & Record<string, unknown>): Promise<StudyRecord> {
  const response = await request<BrapiListResponse<Study>>("/brapi/v2/studies", {
    method: "POST",
    body: JSON.stringify([toRequestBody(payload)]),
  });
  return mapStudy(response.result.data[0]);
}

export async function updateStudyRow(id: string, payload: StudyPayload & Record<string, unknown>): Promise<StudyRecord> {
  const response = await request<BrapiSingleResponse<Study>>(`/brapi/v2/studies/${encodeURIComponent(id)}`, {
    method: "PUT",
    body: JSON.stringify(toRequestBody(payload)),
  });
  return mapStudy(response.result);
}

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