import { DEFAULT_PAGE_QUERY } from "@/constants/api";
import { getAuthToken } from "@/utils/token";

export interface SelectOption {
  value: string;
  label: string;
}

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

interface CommonCropNamesResponse {
  result?: { data?: string[] };
}

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

interface ProgramResponse {
  programDbId: string;
  programName: string | null;
  name: string | null;
  abbreviation: string | null;
}

interface GermplasmResponse {
  germplasmDbId: string;
  germplasmName: string | null;
  defaultDisplayName: string | null;
  accessionNumber: string | null;
}

interface CrossResponse {
  crossDbId: string;
  crossName: string | null;
}

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

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

interface TrialResponse {
  trialDbId: string;
  trialName: string | null;
}

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

interface ObservationUnitResponse {
  observationUnitDbId: string;
  observationUnitName: string | null;
}

interface BreedingMethodResponse {
  breedingMethodDbId: string;
  breedingMethodName: string | null;
  name: string | null;
  abbreviation: string | null;
}

const PAGE_QUERY = DEFAULT_PAGE_QUERY;

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>;
}

function createCachedLoader<T>(fetcher: () => Promise<T>) {
  let cached: T | null = null;
  let inflight: Promise<T> | null = null;

  return {
    load(force = false): Promise<T> {
      if (!force && cached !== null) return Promise.resolve(cached);
      if (!force && inflight) return inflight;

      inflight = fetcher()
        .then((data) => {
          cached = data;
          inflight = null;
          return data;
        })
        .catch((error) => {
          inflight = null;
          throw error;
        });

      return inflight;
    },
    invalidate() {
      cached = null;
      inflight = null;
    },
    peek(): T | null {
      return cached;
    },
  };
}

const mapLocationOptions = (rows: LocationResponse[]): SelectOption[] =>
  rows.map((location) => ({
    value: location.locationDbId,
    label: `${location.locationName || location.locationDbId}${location.abbreviation ? ` / ${location.abbreviation}` : ""}`,
  }));

const mapProgramOptions = (rows: ProgramResponse[]): SelectOption[] =>
  rows.map((program) => ({
    value: program.programDbId,
    label: `${program.programName || program.name || program.programDbId}${program.abbreviation ? ` / ${program.abbreviation}` : ""}`,
  }));

const mapGermplasmOptions = (rows: GermplasmResponse[]): SelectOption[] =>
  rows.map((germplasm) => {
    const base = germplasm.defaultDisplayName || germplasm.germplasmName || germplasm.germplasmDbId;
    const suffix = germplasm.accessionNumber ? ` / ${germplasm.accessionNumber}` : "";
    return { value: germplasm.germplasmDbId, label: `${base}${suffix}` };
  });

const mapCrossOptions = (rows: CrossResponse[]): SelectOption[] =>
  rows.map((cross) => ({
    value: cross.crossDbId,
    label: cross.crossName || cross.crossDbId,
  }));

const mapPersonOptions = (rows: PersonResponse[]): SelectOption[] =>
  rows.map((person) => {
    const name = [person.firstName, person.lastName].filter(Boolean).join(" ").trim();
    return {
      value: person.personDbId,
      label: name || person.email || person.personDbId,
    };
  });

const mapSeasonOptions = (rows: SeasonResponse[]): SelectOption[] =>
  rows.map((season) => ({
    value: season.seasonDbId,
    label: season.seasonName || String(season.year ?? season.seasonDbId),
  }));

const mapTrialOptions = (rows: TrialResponse[]): SelectOption[] =>
  rows.map((trial) => ({
    value: trial.trialDbId,
    label: trial.trialName || trial.trialDbId,
  }));

const mapStudyOptions = (rows: StudyResponse[]): SelectOption[] =>
  rows.map((study) => ({
    value: study.studyDbId,
    label: study.studyName || study.studyDbId,
  }));

const mapObservationUnitOptions = (rows: ObservationUnitResponse[]): SelectOption[] =>
  rows.map((unit) => ({
    value: unit.observationUnitDbId,
    label: unit.observationUnitName || unit.observationUnitDbId,
  }));

const mapBreedingMethodOptions = (rows: BreedingMethodResponse[]): SelectOption[] =>
  rows.map((method) => {
    const base = method.breedingMethodName || method.name || method.breedingMethodDbId;
    return {
      value: method.breedingMethodDbId,
      label: method.abbreviation ? `${base} / ${method.abbreviation}` : base,
    };
  });

const locationLoader = createCachedLoader(async () => {
  const response = await request<BrapiListResponse<LocationResponse>>(`/brapi/v2/locations?${PAGE_QUERY}`);
  return mapLocationOptions(response.result.data);
});

const programLoader = createCachedLoader(async () => {
  const response = await request<BrapiListResponse<ProgramResponse>>(`/brapi/v2/programs?${PAGE_QUERY}`);
  return mapProgramOptions(response.result.data);
});

const germplasmLoader = createCachedLoader(async () => {
  const response = await request<BrapiListResponse<GermplasmResponse>>(`/brapi/v2/germplasm?${PAGE_QUERY}`);
  return mapGermplasmOptions(response.result.data);
});

const crossLoader = createCachedLoader(async () => {
  const response = await request<BrapiListResponse<CrossResponse>>(`/brapi/v2/crosses?${PAGE_QUERY}`);
  return mapCrossOptions(response.result.data);
});

const personLoader = createCachedLoader(async () => {
  const response = await request<BrapiListResponse<PersonResponse>>(`/brapi/v2/people?${PAGE_QUERY}`);
  return mapPersonOptions(response.result.data);
});

const seasonLoader = createCachedLoader(async () => {
  const response = await request<BrapiListResponse<SeasonResponse>>(`/brapi/v2/seasons?${PAGE_QUERY}`);
  return mapSeasonOptions(response.result.data);
});

const trialLoader = createCachedLoader(async () => {
  const response = await request<BrapiListResponse<TrialResponse>>(`/brapi/v2/trials?${PAGE_QUERY}`);
  return mapTrialOptions(response.result.data);
});

const studyLoader = createCachedLoader(async () => {
  const response = await request<BrapiListResponse<StudyResponse>>(`/brapi/v2/studies?${PAGE_QUERY}`);
  return mapStudyOptions(response.result.data);
});

const observationUnitLoader = createCachedLoader(async () => {
  const response = await request<BrapiListResponse<ObservationUnitResponse>>(
    `/brapi/v2/observationunits?${PAGE_QUERY}`,
  );
  return mapObservationUnitOptions(response.result.data);
});

const breedingMethodLoader = createCachedLoader(async () => {
  const response = await request<BrapiListResponse<BreedingMethodResponse>>(
    `/brapi/v2/breedingmethods?${PAGE_QUERY}`,
  );
  return mapBreedingMethodOptions(response.result.data);
});

const commonCropNameLoader = createCachedLoader(async () => {
  const response = await request<CommonCropNamesResponse>(`/brapi/v2/commoncropnames?${PAGE_QUERY}`);
  return (response.result?.data ?? []).map((cropName) => ({
    value: cropName,
    label: cropName,
  }));
});

export const loadLocationOptions = (force = false) => locationLoader.load(force);
export const loadProgramOptions = (force = false) => programLoader.load(force);
export const loadGermplasmOptions = (force = false) => germplasmLoader.load(force);
export const loadCrossOptions = (force = false) => crossLoader.load(force);
export const loadPersonOptions = (force = false) => personLoader.load(force);
export const loadSeasonOptions = (force = false) => seasonLoader.load(force);
export const loadTrialOptions = (force = false) => trialLoader.load(force);
export const loadStudyOptions = (force = false) => studyLoader.load(force);
export const loadObservationUnitOptions = (force = false) => observationUnitLoader.load(force);
export const loadBreedingMethodOptions = (force = false) => breedingMethodLoader.load(force);
export const loadCommonCropNameOptions = (force = false) => commonCropNameLoader.load(force);

export function invalidateLocationOptions() { locationLoader.invalidate(); }
export function invalidateProgramOptions() { programLoader.invalidate(); }
export function invalidateGermplasmOptions() { germplasmLoader.invalidate(); }
export function invalidateCrossOptions() { crossLoader.invalidate(); }
export function invalidatePersonOptions() { personLoader.invalidate(); }
export function invalidateSeasonOptions() { seasonLoader.invalidate(); }
export function invalidateTrialOptions() { trialLoader.invalidate(); }
export function invalidateStudyOptions() { studyLoader.invalidate(); }
export function invalidateObservationUnitOptions() { observationUnitLoader.invalidate(); }
export function invalidateBreedingMethodOptions() { breedingMethodLoader.invalidate(); }
export function invalidateCommonCropNameOptions() { commonCropNameLoader.invalidate(); }

export function invalidateAllDropdownCache() {
  locationLoader.invalidate();
  programLoader.invalidate();
  germplasmLoader.invalidate();
  crossLoader.invalidate();
  personLoader.invalidate();
  seasonLoader.invalidate();
  trialLoader.invalidate();
  studyLoader.invalidate();
  observationUnitLoader.invalidate();
  breedingMethodLoader.invalidate();
  commonCropNameLoader.invalidate();
}

/** ??????????? in-flight ?? */
export async function loadDropdownBundle(keys: {
  locations?: boolean;
  programs?: boolean;
  germplasms?: boolean;
  crosses?: boolean;
  people?: boolean;
  seasons?: boolean;
  trials?: boolean;
  studies?: boolean;
  observationUnits?: boolean;
  breedingMethods?: boolean;
  commonCropNames?: boolean;
}, force = false) {
  const [locations, programs, germplasms, crosses, people, seasons, trials, studies, observationUnits, breedingMethods, commonCropNames] =
    await Promise.all([
      keys.locations ? loadLocationOptions(force) : Promise.resolve(null),
      keys.programs ? loadProgramOptions(force) : Promise.resolve(null),
      keys.germplasms ? loadGermplasmOptions(force) : Promise.resolve(null),
      keys.crosses ? loadCrossOptions(force) : Promise.resolve(null),
      keys.people ? loadPersonOptions(force) : Promise.resolve(null),
      keys.seasons ? loadSeasonOptions(force) : Promise.resolve(null),
      keys.trials ? loadTrialOptions(force) : Promise.resolve(null),
      keys.studies ? loadStudyOptions(force) : Promise.resolve(null),
      keys.observationUnits ? loadObservationUnitOptions(force) : Promise.resolve(null),
      keys.breedingMethods ? loadBreedingMethodOptions(force) : Promise.resolve(null),
      keys.commonCropNames ? loadCommonCropNameOptions(force) : Promise.resolve(null),
    ]);

  return {
    locations: locations ?? [],
    programs: programs ?? [],
    germplasms: germplasms ?? [],
    crosses: crosses ?? [],
    people: people ?? [],
    seasons: seasons ?? [],
    trials: trials ?? [],
    studies: studies ?? [],
    observationUnits: observationUnits ?? [],
    breedingMethods: breedingMethods ?? [],
    commonCropNames: commonCropNames ?? [],
  };
}

export { createCachedLoader, request as dropdownCacheRequest };
