/**
 * Janice API integration for EVE Online market appraisals and pricing.
 * This module provides interfaces and functions to interact with the Janice API.
 */

const BASE_URL = 'https://janice.e-351.com/api/rest/v2';

/**
 * Represents an appraisal from the Janice API.
 */
export interface Appraisal {
  /** Unique identifier for the appraisal */
  id: number;
  /** Creation timestamp */
  created: string;
  /** Expiration timestamp */
  expires: string;
  /** Dataset timestamp */
  datasetTime: string;
  /** Appraisal code */
  code: string;
  /** Designation type */
  designation: AppraisalDesignation;
  /** Pricing strategy */
  pricing: AppraisalPricing;
  /** Pricing variant */
  pricingVariant: AppraisalPricingVariant;
  /** Price percentage */
  pricePercentage: number;
  /** Whether the appraisal is compactized */
  isCompactized: boolean;
  /** Failure messages */
  failures: string;
  /** Market information */
  market: PricerMarket;
  /** Total volume */
  totalVolume: number;
  /** Total packaged volume */
  totalPackagedVolume: number;
  /** Effective prices */
  effectivePrices: AppraisalValues;
  /** Immediate prices */
  immediatePrices: AppraisalValues;
  /** Top 5 average prices */
  top5AveragePrices: AppraisalValues;
  /** List of items in the appraisal */
  items: AppraisalItem[];
}

/**
 * Price values for an appraisal.
 */
export interface AppraisalValues {
  /** Total buy price */
  totalBuyPrice: number;
  /** Total split price */
  totalSplitPrice: number;
  /** Total sell price */
  totalSellPrice: number;
}

/**
 * Represents an item in an appraisal.
 */
export interface AppraisalItem {
  /** Item ID */
  id: number;
  /** Amount of the item */
  amount: number;
  /** Number of buy orders */
  buyOrderCount: number;
  /** Buy volume */
  buyVolume: number;
  /** Number of sell orders */
  sellOrderCount: number;
  /** Sell volume */
  sellVolume: number;
  /** Effective prices for the item */
  effectivePrices: AppraisalItemValues;
  /** Immediate prices for the item */
  immediatePrices: AppraisalItemValues;
  /** Top 5 average prices for the item */
  top5AveragePrices: AppraisalItemValues;
  /** Total volume */
  totalVolume: number;
  /** Total packaged volume */
  totalPackagedVolume: number;
  /** Item type information */
  itemType: ItemType;
}

/**
 * Represents a pricer item from the API.
 */
export interface PricerItem {
  /** Date of the price data */
  date: string;
  /** Market information */
  market: PricerMarket;
  /** Number of buy orders */
  buyOrderCount: number;
  /** Buy volume */
  buyVolume: number;
  /** Number of sell orders */
  sellOrderCount: number;
  /** Sell volume */
  sellVolume: number;
  /** Immediate prices */
  immediatePrices: PricerItemValues;
  /** Top 5 average prices */
  top5AveragePrices: PricerItemValues;
  /** Item type information */
  itemType: ItemType;
}

/**
 * Price values for a pricer item.
 */
export interface PricerItemValues {
  /** Buy price */
  buyPrice: number;
  /** Split price */
  splitPrice: number;
  /** Sell price */
  sellPrice: number;
  /** 5-day median buy price */
  buyPrice5DayMedian: number;
  /** 5-day median split price */
  splitPrice5DayMedian: number;
  /** 5-day median sell price */
  sellPrice5DayMedian: number;
  /** 30-day median buy price */
  buyPrice30DayMedian: number;
  /** 30-day median split price */
  splitPrice30DayMedian: number;
  /** 30-day median sell price */
  sellPrice30DayMedian: number;
}

/**
 * Extended price values for appraisal items.
 */
export interface AppraisalItemValues extends PricerItemValues {
  /** Total buy price */
  buyPriceTotal: number;
  /** Total split price */
  splitPriceTotal: number;
  /** Total sell price */
  sellPriceTotal: number;
}

/**
 * Represents an item type.
 */
export interface ItemType {
  /** EVE item ID */
  eid: number;
  /** Item name (optional) */
  name?: string;
  /** Item volume */
  volume: number;
  /** Packaged volume */
  packagedVolume: number;
}

/**
 * Enumeration for appraisal designations.
 */
export enum AppraisalDesignation {
  Appraisal = 'appraisal',
  WantToBuy = 'wtb',
  WantToSell = 'wts',
}

/**
 * Enumeration for appraisal pricing strategies.
 */
export enum AppraisalPricing {
  Buy = 'buy',
  Split = 'split',
  Sell = 'sell',
  Purchase = 'purchase',
}

/**
 * Enumeration for appraisal pricing variants.
 */
export enum AppraisalPricingVariant {
  Immediate = 'immediate',
  Top5Percent = 'top5percent',
}

/**
 * Represents a market in the pricer system.
 */
export interface PricerMarket {
  /** Market ID */
  id: number;
  /** Market name */
  name: string;
}

/**
 * Predefined list of available markets.
 */
export const markets: PricerMarket[] = [
  { id: 2, name: 'Jita 4-4' },
  { id: 3, name: 'R1O-GN' },
  { id: 6, name: 'NPC' },
  { id: 114, name: 'MJ-5F9' },
  { id: 115, name: 'Amarr' },
  { id: 116, name: 'Rens' },
  { id: 117, name: 'Dodixie' },
  { id: 118, name: 'Hek' },
] as const;

/**
 * Simple cache for API responses to improve performance.
 */
const cache = new Map<string, { data: any; timestamp: number }>();
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes

/**
 * Clears the internal cache. Useful for testing.
 */
export function clearCache(): void {
  cache.clear();
}

/**
 * Validates if a value is a positive number.
 */
export function isPositiveNumber(value: any): value is number {
  return typeof value === 'number' && value > 0 && isFinite(value);
}

/**
 * Validates if a value is a non-empty string.
 */
export function isNonEmptyString(value: any): value is string {
  return typeof value === 'string' && value.trim().length > 0;
}

/**
 * Fetches price data for a single item type.
 * @param type_id - The EVE item type ID
 * @param market_id - The market ID (default: 2 for Jita)
 * @returns Promise resolving to PricerItem
 * @throws Error if API call fails or validation fails
 */
export const fetchPrice = async (type_id: number, market_id: number = 2): Promise<PricerItem> => {
  if (!isPositiveNumber(type_id)) {
    throw new Error('Invalid type_id: must be a positive number');
  }
  if (!isPositiveNumber(market_id)) {
    throw new Error('Invalid market_id: must be a positive number');
  }

  const cacheKey = `price_${type_id}_${market_id}`;
  const cached = cache.get(cacheKey);
  if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
    return cached.data;
  }

  try {
    const response = await fetch(`${BASE_URL}/pricer/${type_id}?market=${market_id}`, {
      method: 'GET',
      headers: {
        'X-ApiKey': process.env.JANICE_KEY || '',
        'Accept': 'application/json',
      },
    });

    if (!response.ok) {
      throw new Error(`API request failed: ${response.status} ${response.statusText}`);
    }

    const data = await response.json();
    cache.set(cacheKey, { data, timestamp: Date.now() });
    return data as PricerItem;
  } catch (error) {
    console.error(`Error fetching price for type_id ${type_id}:`, error);
    throw error;
  }
};

/**
 * Fetches price data for multiple item types.
 * @param type_ids - Array of EVE item type IDs
 * @param market_id - The market ID (default: 2 for Jita)
 * @returns Promise resolving to array of PricerItem
 * @throws Error if API call fails or validation fails
 */
export const fetchPrices = async (type_ids: number[], market_id: number = 2): Promise<PricerItem[]> => {
  if (!Array.isArray(type_ids) || type_ids.length === 0 || !type_ids.every(isPositiveNumber)) {
    throw new Error('Invalid type_ids: must be a non-empty array of positive numbers');
  }
  if (!isPositiveNumber(market_id)) {
    throw new Error('Invalid market_id: must be a positive number');
  }

  const cacheKey = `prices_${type_ids.sort().join('_')}_${market_id}`;
  const cached = cache.get(cacheKey);
  if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
    return cached.data;
  }

  try {
    const response = await fetch(`${BASE_URL}/pricer?market=${market_id}`, {
      method: 'POST',
      headers: {
        'Content-Type': 'text/plain',
        'X-ApiKey': process.env.JANICE_KEY || '',
        'Accept': 'application/json',
      },
      body: type_ids.join('\n'),
    });

    if (!response.ok) {
      throw new Error(`API request failed: ${response.status} ${response.statusText}`);
    }

    const data = await response.json();
    cache.set(cacheKey, { data, timestamp: Date.now() });
    return data as PricerItem[];
  } catch (error) {
    console.error(`Error fetching prices for type_ids ${type_ids}:`, error);
    throw error;
  }
};

/**
 * Fetches an appraisal by its code.
 * @param code - The appraisal code
 * @returns Promise resolving to Appraisal
 * @throws Error if API call fails or validation fails
 */
export const fetchAppraisal = async (code: string): Promise<Appraisal> => {
  if (!isNonEmptyString(code)) {
    throw new Error('Invalid code: must be a non-empty string');
  }

  const cacheKey = `appraisal_${code}`;
  const cached = cache.get(cacheKey);
  if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
    return cached.data;
  }

  try {
    const response = await fetch(`${BASE_URL}/appraisal/${code}`, {
      method: 'GET',
      headers: {
        'X-ApiKey': process.env.JANICE_KEY || '',
        'Accept': 'application/json',
      },
    });

    if (!response.ok) {
      throw new Error(`API request failed: ${response.status} ${response.statusText}`);
    }

    const data = await response.json();
    cache.set(cacheKey, { data, timestamp: Date.now() });
    return data as Appraisal;
  } catch (error) {
    console.error(`Error fetching appraisal for code ${code}:`, error);
    throw error;
  }
};

/**
 * Appraises items from text input.
 * @param text - The text containing items to appraise
 * @param market_id - The market ID (default: 2 for Jita)
 * @returns Promise resolving to Appraisal
 * @throws Error if API call fails or validation fails
 */
export const appraiseItems = async (text: string, market_id: number = 2): Promise<Appraisal> => {
  if (!isNonEmptyString(text)) {
    throw new Error('Invalid text: must be a non-empty string');
  }
  if (!isPositiveNumber(market_id)) {
    throw new Error('Invalid market_id: must be a positive number');
  }

  try {
    const response = await fetch(`${BASE_URL}/appraisal?market=${market_id}&persist=true&compactize=true&pricePercentage=1`, {
      method: 'POST',
      headers: {
        'Content-Type': 'text/plain',
        'X-ApiKey': process.env.JANICE_KEY || '',
        'Accept': 'application/json',
      },
      body: text,
    });

    if (!response.ok) {
      throw new Error(`API request failed: ${response.status} ${response.statusText}`);
    }

    const data = await response.json();
    return data as Appraisal;
  } catch (error) {
    console.error('Error appraising items:', error);
    throw error;
  }
};
