import { isOptionalApiRouteMissing } from "./apiErrorUtils";
import { isRecord, serverRequest } from "./serverConnection";
import type { EnterpriseVideoPricingConfig, EnterpriseVideoPricingRule } from "../utils/enterpriseVideoPolicy";

export interface PublicModelPrice {
  id?: number | string;
  modelKey: string;
  displayName?: string;
  category?: string;
  pricingType?: string;
  inputPriceMills: number | null;
  outputPriceMills: number | null;
  flatPriceMills: number | null;
  currency: string;
  enabled: boolean;
  createdAt?: string;
  updatedAt?: string;
}

export interface PublicPricingPayload {
  modelPrices: PublicModelPrice[];
  enterpriseVideoPricing: EnterpriseVideoPricingConfig | null;
}

function readString(
  record: Record<string, unknown>,
  keys: string[],
): string | undefined {
  for (const key of keys) {
    const value = record[key];
    if (typeof value === "string" && value.trim()) return value.trim();
  }
  return undefined;
}

function readNumber(
  record: Record<string, unknown>,
  keys: string[],
): number | null {
  for (const key of keys) {
    const value = record[key];
    const parsed =
      typeof value === "number"
        ? value
        : typeof value === "string"
          ? Number(value)
          : NaN;
    if (Number.isFinite(parsed)) return parsed;
  }
  return null;
}

function readBoolean(
  record: Record<string, unknown>,
  keys: string[],
  fallback: boolean,
): boolean {
  for (const key of keys) {
    const value = record[key];
    if (typeof value === "boolean") return value;
    if (typeof value === "number") return value !== 0;
    if (typeof value === "string") {
      const normalized = value.trim().toLowerCase();
      if (["1", "true", "yes", "enabled"].includes(normalized)) return true;
      if (["0", "false", "no", "disabled"].includes(normalized)) return false;
    }
  }
  return fallback;
}

function readStringArray(record: Record<string, unknown>, keys: string[]): string[] {
  for (const key of keys) {
    const value = record[key];
    if (!Array.isArray(value)) continue;
    return value
      .map((item) => (typeof item === "string" ? item.trim() : ""))
      .filter(Boolean);
  }
  return [];
}

function normalizeRateMap(raw: unknown): Record<string, number> | null {
  if (!isRecord(raw)) return null;
  const result: Record<string, number> = {};
  for (const [key, value] of Object.entries(raw)) {
    const parsed = typeof value === "number" ? value : typeof value === "string" ? Number(value) : NaN;
    if (Number.isFinite(parsed) && parsed >= 0) result[key] = parsed;
  }
  return Object.keys(result).length ? result : null;
}

function normalizeEnterpriseVideoPricingRule(raw: unknown): EnterpriseVideoPricingRule | null {
  if (!isRecord(raw)) return null;
  const id = readString(raw, ["id", "key", "name"]);
  const modelIncludes = readStringArray(raw, ["modelIncludes", "model_includes", "modelPatterns", "model_patterns"]);
  const rates = normalizeRateMap(raw.rates);
  if (!id || modelIncludes.length === 0 || !rates) return null;

  const when = isRecord(raw.when)
    ? {
        ...(typeof raw.when.muted === "boolean" ? { muted: raw.when.muted } : {}),
        ...(typeof raw.when.hasReferenceVideo === "boolean"
          ? { hasReferenceVideo: raw.when.hasReferenceVideo }
          : {}),
      }
    : undefined;

  return {
    id,
    modelIncludes,
    ...(when && Object.keys(when).length ? { when } : {}),
    rates,
  };
}

export function normalizePublicModelPrice(
  raw: unknown,
): PublicModelPrice | null {
  if (!isRecord(raw)) return null;

  const modelKey = readString(raw, ["modelKey", "model_key", "key", "model"]);
  if (!modelKey) return null;

  const displayName = readString(raw, ["displayName", "display_name", "name"]);
  const category = readString(raw, ["category", "type"]);
  const pricingType = readString(raw, ["pricingType", "pricing_type"]);
  const currency = readString(raw, ["currency"]) || "CNY";
  const createdAt = readString(raw, ["createdAt", "created_at"]);
  const updatedAt = readString(raw, ["updatedAt", "updated_at"]);
  const idValue = raw.id;

  return {
    id:
      typeof idValue === "number" || typeof idValue === "string"
        ? idValue
        : undefined,
    modelKey,
    displayName,
    category,
    pricingType,
    inputPriceMills: readNumber(raw, ["inputPriceMills", "input_price_mills"]),
    outputPriceMills: readNumber(raw, [
      "outputPriceMills",
      "output_price_mills",
    ]),
    flatPriceMills: readNumber(raw, ["flatPriceMills", "flat_price_mills"]),
    currency,
    enabled: readBoolean(raw, ["enabled", "is_enabled"], true),
    createdAt,
    updatedAt,
  };
}

export function normalizePublicModelPrices(
  payload: unknown,
): PublicModelPrice[] {
  const rawPrices = Array.isArray(payload)
    ? payload
    : isRecord(payload) && Array.isArray(payload.prices)
      ? payload.prices
      : isRecord(payload) && Array.isArray(payload.modelPrices)
        ? payload.modelPrices
        : isRecord(payload) && Array.isArray(payload.model_prices)
          ? payload.model_prices
          : isRecord(payload) && Array.isArray(payload.models)
            ? payload.models
            : [];

  return rawPrices
    .map((item) => normalizePublicModelPrice(item))
    .filter((item): item is PublicModelPrice => Boolean(item));
}

export function normalizeEnterpriseVideoPricingConfig(raw: unknown): EnterpriseVideoPricingConfig | null {
  if (!isRecord(raw)) return null;
  const rules = Array.isArray(raw.rules)
    ? raw.rules
        .map((item) => normalizeEnterpriseVideoPricingRule(item))
        .filter((item): item is EnterpriseVideoPricingRule => Boolean(item))
    : [];
  if (rules.length === 0) return null;

  const creditsPerCny = readNumber(raw, ["creditsPerCny", "credits_per_cny"]);
  const defaultResolution = readString(raw, ["defaultResolution", "default_resolution"]);
  const billingUnit = readString(raw, ["billingUnit", "billing_unit"]);
  const currency = readString(raw, ["currency"]);
  const resolutions = readStringArray(raw, ["resolutions", "supportedResolutions", "supported_resolutions"]);

  return {
    ...(currency ? { currency } : {}),
    ...(creditsPerCny !== null ? { creditsPerCny } : {}),
    ...(billingUnit ? { billingUnit } : {}),
    ...(defaultResolution ? { defaultResolution } : {}),
    ...(resolutions.length ? { resolutions } : {}),
    rules,
  };
}

export function normalizePublicPricingPayload(payload: unknown): PublicPricingPayload {
  const enterpriseVideoPricingRaw =
    isRecord(payload) && (payload.enterpriseVideoPricing ?? payload.enterprise_video_pricing);

  return {
    modelPrices: normalizePublicModelPrices(payload),
    enterpriseVideoPricing: normalizeEnterpriseVideoPricingConfig(enterpriseVideoPricingRaw),
  };
}

let cachedPricing: PublicPricingPayload | null = null;
let pricesRouteMissing = false;

export const publicPricingClient = {
  async getPricing(): Promise<PublicPricingPayload> {
    if (cachedPricing) return cachedPricing;
    if (pricesRouteMissing) return { modelPrices: [], enterpriseVideoPricing: null };

    try {
      const payload = await serverRequest<unknown>("prices", {
        fallbackMessage: "Model prices request failed",
      });
      cachedPricing = normalizePublicPricingPayload(payload);
      return cachedPricing;
    } catch (error) {
      if (isOptionalApiRouteMissing(error)) {
        pricesRouteMissing = true;
        return { modelPrices: [], enterpriseVideoPricing: null };
      }
      throw error;
    }
  },

  async getPrices(): Promise<PublicModelPrice[]> {
    const pricing = await publicPricingClient.getPricing();
    return pricing.modelPrices;
  },
};
