import { toBrapiIsoDateTime } from "@/lib/brapiIso";
import { getAuthToken } from "@/utils/token";
import {
  createCachedLoader,
  loadCrossOptions,
  loadGermplasmOptions,
  loadLocationOptions,
  loadProgramOptions,
} from "@/services/dropdownCache";
import {
  NONE_SELECT_VALUE,
  resolveStockStatus,
  type ContentMixtureRow,
  type SeedLotContentMixtureRecord,
  type SeedLotQuery,
  type SeedLotRecord,
  type SeedLotTransactionQuery,
  type SeedLotTransactionRecord,
  type SelectOption,
  type StockStatus,
  type TransactionActionType,
} 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;
}

type SeedLotPayload = Partial<Record<
  | "id"
  | "name"
  | "seed_lot_description"
  | "amount"
  | "units"
  | "created_date"
  | "last_updated"
  | "source_collection"
  | "storage_location"
  | "location_id"
  | "program_id"
  | "primary_germplasm_id"
  | "content_mixture",
  unknown
>>;

export interface TransactionPayload {
  action: TransactionActionType;
  amount: unknown;
  description?: unknown;
  timestamp?: unknown;
  units?: unknown;
  from_seed_lot_id?: unknown;
  to_seed_lot_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>;
}

export const optionalText = (value: unknown) => {
  const normalized = String(value ?? "").trim();
  if (!normalized || normalized === NONE_SELECT_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 seedLotName = (payload: SeedLotPayload) => {
  const name = optionalText(payload.name);
  if (!name) throw new Error("请填写批次名");
  return name;
};

const parseMixtureRows = (value: unknown): ContentMixtureRow[] => {
  if (!Array.isArray(value)) return [];
  return value.map((row) => {
    const item = row as Record<string, unknown>;
    return {
      germplasm_id: String(item.germplasm_id ?? NONE_SELECT_VALUE),
      cross_id: String(item.cross_id ?? NONE_SELECT_VALUE),
      mixture_percentage: String(item.mixture_percentage ?? ""),
    };
  });
};

export const mapContentMixtureToForm = (mixtures: SeedLotContentMixtureRecord[] | null | undefined): ContentMixtureRow[] => {
  if (!mixtures?.length) {
    return [{ germplasm_id: NONE_SELECT_VALUE, cross_id: NONE_SELECT_VALUE, mixture_percentage: "100" }];
  }
  return mixtures.map((mixture) => ({
    germplasm_id: mixture.germplasmDbId || mixture.germplasm_id || NONE_SELECT_VALUE,
    cross_id: mixture.crossDbId || mixture.cross_id || NONE_SELECT_VALUE,
    mixture_percentage: String(mixture.mixturePercentage ?? mixture.mixture_percentage ?? ""),
  }));
};

export const buildContentMixturePayload = (payload: SeedLotPayload) => {
  let rows = parseMixtureRows(payload.content_mixture);
  const primaryGermplasmId = optionalText(payload.primary_germplasm_id);

  if (rows.length === 0 && primaryGermplasmId) {
    rows = [{ germplasm_id: primaryGermplasmId, cross_id: NONE_SELECT_VALUE, mixture_percentage: "100" }];
  }

  if (rows.length === 0) {
    throw new Error("请至少录入一条批次组成，或选择主材");
  }

  const normalized = rows.map((row) => {
    const germplasmDbId = optionalText(row.germplasm_id);
    const crossDbId = optionalText(row.cross_id);
    const mixturePercentage = optionalNumber(row.mixture_percentage);

    if (!germplasmDbId && !crossDbId) {
      throw new Error("批次组成每行需选择材料或杂交来源");
    }
    if (mixturePercentage === null || mixturePercentage < 0 || mixturePercentage > 100) {
      throw new Error("批次组成占比需在 0 到 100 之间");
    }

    return {
      ...(germplasmDbId ? { germplasmDbId } : {}),
      ...(crossDbId ? { crossDbId } : {}),
      mixturePercentage,
    };
  });

  const totalPercentage = normalized.reduce((sum, row) => sum + (row.mixturePercentage ?? 0), 0);
  if (Math.abs(totalPercentage - 100) > 0.01) {
    throw new Error(`批次组成占比合计应为 100%，当前为 ${totalPercentage}%`);
  }

  return normalized;
};

const mapSeedLot = (seedLot: SeedLotRecord): SeedLotRecord => {
  const contentMixture = seedLot.contentMixture ?? seedLot.content_mixture ?? [];
  return {
    ...seedLot,
    id: seedLot.seedLotDbId || seedLot.id,
    name: seedLot.name || seedLot.seed_lot_name || seedLot.seedLotName,
    seed_lot_name: seedLot.seed_lot_name || seedLot.seedLotName || seedLot.name,
    seed_lot_description: seedLot.seed_lot_description || seedLot.seedLotDescription || seedLot.description,
    created_date: seedLot.created_date || seedLot.createdDate,
    last_updated: seedLot.last_updated || seedLot.lastUpdated,
    source_collection: seedLot.source_collection || seedLot.sourceCollection,
    storage_location: seedLot.storage_location || seedLot.storageLocation,
    location_id: seedLot.location_id || seedLot.locationDbId,
    location_name: seedLot.location_name || seedLot.locationName,
    program_id: seedLot.program_id || seedLot.programDbId,
    program_name: seedLot.program_name || seedLot.programName,
    content_mixture: contentMixture,
    contentMixture,
  };
};

const mapTransaction = (
  transaction: SeedLotTransactionRecord,
  seedLotNameById: Map<string, string>,
): SeedLotTransactionRecord => {
  const id = transaction.transactionDbId || transaction.id;
  const fromId = transaction.fromSeedLotDbId || transaction.from_seed_lot_id || null;
  const toId = transaction.toSeedLotDbId || transaction.to_seed_lot_id || null;
  return {
    ...transaction,
    id: id || "",
    from_seed_lot_id: fromId,
    to_seed_lot_id: toId,
    description: transaction.transactionDescription || transaction.description,
    timestamp: transaction.transactionTimestamp || transaction.timestamp,
    from_seed_lot_name: fromId ? seedLotNameById.get(fromId) ?? fromId : null,
    to_seed_lot_name: toId ? seedLotNameById.get(toId) ?? toId : null,
  };
};

const toRequestBody = (payload: SeedLotPayload, options?: { includeAmount?: boolean }) => {
  const body: Record<string, unknown> = {
    seedLotName: seedLotName(payload),
    units: optionalText(payload.units),
    contentMixture: buildContentMixturePayload(payload).map((row) => ({
      ...row,
      mixturePercentage: Math.round(row.mixturePercentage ?? 0),
    })),
  };

  const seedLotDescription = optionalText(payload.seed_lot_description);
  const createdDate = toBrapiIsoDateTime(payload.created_date);
  const lastUpdated = toBrapiIsoDateTime(payload.last_updated);
  const sourceCollection = optionalText(payload.source_collection);
  const storageLocation = optionalText(payload.storage_location);
  const locationDbId = optionalText(payload.location_id);
  const programDbId = optionalText(payload.program_id);
  const amount = optionalNumber(payload.amount);

  if (options?.includeAmount !== false && amount !== null) body.amount = amount;
  if (seedLotDescription) body.seedLotDescription = seedLotDescription;
  if (createdDate) body.createdDate = createdDate;
  if (lastUpdated) body.lastUpdated = lastUpdated;
  if (sourceCollection) body.sourceCollection = sourceCollection;
  if (storageLocation) body.storageLocation = storageLocation;
  if (locationDbId) body.locationDbId = locationDbId;
  if (programDbId) body.programDbId = programDbId;

  return body;
};

export function normalizeSeedLotFormData(record: SeedLotRecord): Record<string, unknown> {
  const mixtures = mapContentMixtureToForm(record.content_mixture ?? record.contentMixture);
  const primaryGermplasm = mixtures.length === 1 ? mixtures[0].germplasm_id : NONE_SELECT_VALUE;

  return {
    id: record.id,
    name: record.name ?? "",
    amount: record.amount ?? "",
    units: record.units && record.units !== NONE_SELECT_VALUE ? record.units : NONE_SELECT_VALUE,
    program_id: record.program_id && record.program_id !== NONE_SELECT_VALUE ? record.program_id : NONE_SELECT_VALUE,
    location_id: record.location_id && record.location_id !== NONE_SELECT_VALUE ? record.location_id : NONE_SELECT_VALUE,
    storage_location: record.storage_location ?? "",
    source_collection: record.source_collection ?? "",
    created_date: record.created_date ?? "",
    last_updated: record.last_updated ?? "",
    seed_lot_description: record.seed_lot_description ?? "",
    primary_germplasm_id: primaryGermplasm,
    content_mixture: mixtures,
  };
}

const filterSeedLotRows = (rows: SeedLotRecord[], query?: SeedLotQuery) => {
  if (!query) return rows;

  const programFilter = optionalText(query.program_id);
  const locationFilter = optionalText(query.location_id);
  const nameFilter = optionalText(query.name)?.toLowerCase();
  const statusFilter = optionalText(query.stock_status) as StockStatus | null;

  return rows.filter((row) => {
    if (programFilter && row.program_id !== programFilter) return false;
    if (locationFilter && row.location_id !== locationFilter) return false;
    if (nameFilter && !String(row.name ?? "").toLowerCase().includes(nameFilter)) return false;
    if (statusFilter && resolveStockStatus(row.amount) !== statusFilter) return false;
    return true;
  });
};

const seedLotRowsLoader = createCachedLoader(async () => {
  const response = await request<BrapiListResponse<SeedLotRecord>>("/brapi/v2/seedlots?page=0&pageSize=1000");
  return response.result.data.map(mapSeedLot);
});

export function invalidateSeedLotRowsCache() {
  seedLotRowsLoader.invalidate();
}

export async function fetchSeedLotRows(query?: SeedLotQuery, force = false): Promise<SeedLotRecord[]> {
  const rows = await seedLotRowsLoader.load(force);
  return filterSeedLotRows(rows, query);
}

export async function fetchSeedLotDetail(id: string): Promise<SeedLotRecord> {
  const response = await request<BrapiSingleResponse<SeedLotRecord>>(
    `/brapi/v2/seedlots/${encodeURIComponent(id)}`,
  );
  return mapSeedLot(response.result);
}

export async function fetchSeedLotOptions(force = false): Promise<{
  locations: SelectOption[];
  programs: SelectOption[];
  germplasms: SelectOption[];
  crosses: SelectOption[];
  seedLots: SelectOption[];
}> {
  const [locations, programs, germplasms, crosses, seedLots] = await Promise.all([
    loadLocationOptions(force),
    loadProgramOptions(force),
    loadGermplasmOptions(force),
    loadCrossOptions(force),
    fetchSeedLotRows(undefined, force).catch(() => [] as SeedLotRecord[]),
  ]);

  return {
    locations,
    programs,
    germplasms,
    crosses,
    seedLots: seedLots.map((seedLot) => ({
      value: seedLot.id,
      label: seedLot.name || seedLot.id,
    })),
  };
}

export async function createSeedLotRow(payload: SeedLotPayload): Promise<SeedLotRecord> {
  const units = optionalText(payload.units);
  if (!units) throw new Error("请选择数量单位");

  const response = await request<BrapiListResponse<SeedLotRecord>>("/brapi/v2/seedlots", {
    method: "POST",
    body: JSON.stringify([toRequestBody(payload, { includeAmount: true })]),
  });
  invalidateSeedLotRowsCache();
  return mapSeedLot(response.result.data[0]);
}

export async function updateSeedLotContentMixture(
  seedLotDbId: string,
  contentMixture: ContentMixtureRow[],
): Promise<SeedLotRecord> {
  const detail = await fetchSeedLotDetail(seedLotDbId);
  return updateSeedLotRow(seedLotDbId, {
    name: detail.name ?? "",
    units: detail.units ?? NONE_SELECT_VALUE,
    program_id: detail.program_id ?? NONE_SELECT_VALUE,
    location_id: detail.location_id ?? NONE_SELECT_VALUE,
    storage_location: detail.storage_location ?? "",
    source_collection: detail.source_collection ?? "",
    created_date: detail.created_date ?? "",
    seed_lot_description: detail.seed_lot_description ?? "",
    content_mixture: contentMixture,
  });
}

export async function updateSeedLotMetadata(id: string, payload: SeedLotPayload): Promise<SeedLotRecord> {
  const detail = await fetchSeedLotDetail(id);
  const mixtures = mapContentMixtureToForm(detail.content_mixture ?? detail.contentMixture);
  return updateSeedLotRow(id, {
    ...payload,
    content_mixture: mixtures,
  });
}

export async function updateSeedLotRow(id: string, payload: SeedLotPayload): Promise<SeedLotRecord> {
  const units = optionalText(payload.units);
  if (!units) throw new Error("请选择数量单位");

  const response = await request<BrapiSingleResponse<SeedLotRecord>>(`/brapi/v2/seedlots/${encodeURIComponent(id)}`, {
    method: "PUT",
    body: JSON.stringify(toRequestBody(payload, { includeAmount: false })),
  });
  invalidateSeedLotRowsCache();
  return mapSeedLot(response.result);
}

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

export async function fetchSeedLotTransactions(query?: SeedLotTransactionQuery): Promise<SeedLotTransactionRecord[]> {
  const [transactionsResponse, seedLots] = await Promise.all([
    request<BrapiListResponse<SeedLotTransactionRecord>>(
      "/brapi/v2/seedlots/transactions?page=0&pageSize=1000",
    ),
    fetchSeedLotRows(undefined, true).catch(() => [] as SeedLotRecord[]),
  ]);

  const seedLotNameById = new Map(seedLots.map((row) => [row.id, row.name || row.id]));
  const seedLotById = new Map(seedLots.map((row) => [row.id, row]));
  const rows = transactionsResponse.result.data.map((transaction) => mapTransaction(transaction, seedLotNameById));
  return filterTransactionRows(rows, query, seedLotById);
}

const filterTransactionRows = (
  rows: SeedLotTransactionRecord[],
  query?: SeedLotTransactionQuery,
  seedLotById?: Map<string, SeedLotRecord>,
) => {
  const seedLotFilter = optionalText(query?.seed_lot_id);
  const programFilter = optionalText(query?.program_id);
  const locationFilter = optionalText(query?.location_id);
  const dateFrom = optionalText(query?.date_from);
  const dateTo = optionalText(query?.date_to);
  const keyword = String(query?.keyword ?? "").trim().toLowerCase();

  return rows.filter((row) => {
    if (seedLotFilter && row.from_seed_lot_id !== seedLotFilter && row.to_seed_lot_id !== seedLotFilter) {
      return false;
    }

    if (programFilter || locationFilter) {
      const fromLot = row.from_seed_lot_id ? seedLotById?.get(row.from_seed_lot_id) : undefined;
      const toLot = row.to_seed_lot_id ? seedLotById?.get(row.to_seed_lot_id) : undefined;
      if (programFilter) {
        const programMatches = fromLot?.program_id === programFilter || toLot?.program_id === programFilter;
        if (!programMatches) return false;
      }
      if (locationFilter) {
        const locationMatches = fromLot?.location_id === locationFilter || toLot?.location_id === locationFilter;
        if (!locationMatches) return false;
      }
    }

    const timestamp = String(row.timestamp ?? "");
    if (dateFrom && timestamp.slice(0, 10) < dateFrom) return false;
    if (dateTo && timestamp.slice(0, 10) > dateTo) return false;

    if (keyword) {
      const haystack = [
        row.id,
        row.description,
        row.from_seed_lot_name,
        row.to_seed_lot_name,
        row.from_seed_lot_id,
        row.to_seed_lot_id,
      ].map((value) => String(value ?? "").toLowerCase()).join(" ");
      if (!haystack.includes(keyword)) return false;
    }

    return true;
  });
};

export function inferTransactionAction(transaction: SeedLotTransactionRecord): TransactionActionType | "unknown" {
  const fromId = transaction.from_seed_lot_id;
  const toId = transaction.to_seed_lot_id;
  if (!fromId && toId) return "in";
  if (fromId && !toId) {
    const description = String(transaction.description ?? "").toLowerCase();
    if (description.includes("报废") || description.includes("消")) return "consume";
    return "out";
  }
  if (fromId && toId) {
    const description = String(transaction.description ?? "");
    if (description.includes("分装")) return "split";
    return "transfer";
  }
  return "unknown";
}

export async function createSeedLotTransaction(payload: TransactionPayload, seedLotMap: Map<string, SeedLotRecord>) {
  const amount = optionalNumber(payload.amount);
  if (amount === null || amount <= 0) {
    throw new Error("流转数量必须大于 0");
  }

  const description = optionalText(payload.description);
  const timestamp = optionalText(payload.timestamp) ?? new Date().toISOString();
  const fromId = optionalText(payload.from_seed_lot_id);
  const toId = optionalText(payload.to_seed_lot_id);

  if (fromId && toId && fromId === toId) {
    throw new Error("来源批次与目标批次不能相同");
  }

  let fromSeedLotDbId: string | undefined;
  let toSeedLotDbId: string | undefined;
  let units: string | null = optionalText(payload.units);

  switch (payload.action) {
    case "in":
      if (!toId) throw new Error("入库需选择目标批次");
      toSeedLotDbId = toId;
      units = units || seedLotMap.get(toId)?.units || null;
      break;
    case "out":
    case "consume":
      if (!fromId) throw new Error("出库/消耗需选择来源批次");
      if ((payload.action === "out" || payload.action === "consume") && !description) {
        throw new Error("出库/消耗/报废建议填写流转说明");
      }
      fromSeedLotDbId = fromId;
      units = units || seedLotMap.get(fromId)?.units || null;
      break;
    case "transfer":
    case "split":
      if (!fromId || !toId) throw new Error("转移/分装需同时选择来源与目标批次");
      fromSeedLotDbId = fromId;
      toSeedLotDbId = toId;
      units = units || seedLotMap.get(fromId)?.units || seedLotMap.get(toId)?.units || null;
      break;
    default:
      throw new Error("未知的库存动作");
  }

  if (!fromSeedLotDbId && !toSeedLotDbId) {
    throw new Error("来源批次与目标批次至少填写一项");
  }

  const sourceLot = fromSeedLotDbId ? seedLotMap.get(fromSeedLotDbId) : undefined;
  if (fromSeedLotDbId && sourceLot) {
    const currentAmount = Number(sourceLot.amount ?? 0);
    if (amount > currentAmount) {
      throw new Error(`出库数量不能超过当前库存（${currentAmount}${sourceLot.units ? ` ${sourceLot.units}` : ""}）`);
    }
  }

  if (!units) throw new Error("请指定流转单位");

  const transactionDescription = payload.action === "consume" && description && !description.includes("消")
    ? `消耗/报废：${description}`
    : payload.action === "split" && description && !description.includes("分装")
      ? `分装：${description}`
      : description;

  const response = await request<BrapiListResponse<SeedLotTransactionRecord>>("/brapi/v2/seedlots/transactions", {
    method: "POST",
    body: JSON.stringify([{
      amount,
      units,
      transactionTimestamp: timestamp,
      ...(transactionDescription ? { transactionDescription } : {}),
      ...(fromSeedLotDbId ? { fromSeedLotDbId } : {}),
      ...(toSeedLotDbId ? { toSeedLotDbId } : {}),
    }]),
  });

  invalidateSeedLotRowsCache();
  const seedLotNameById = new Map(
    Array.from(seedLotMap.entries()).map(([key, row]) => [key, row.name || row.id]),
  );
  return mapTransaction(response.result.data[0], seedLotNameById);
}
