import { PaginatedResponse } from './capital_increase';
import { Client } from './client';
import { Region, Locale } from './collections';
import { LaplaceHTTPError } from './errors';

export enum AssetType {
  Stock = 'stock',
  Forex = 'forex',
  Index = 'index',
  Etf = 'etf',
  Commodity = 'commodity',
  StockRights = 'stock_rights'
}

export enum AssetClass {
  Adr = 'adr',
  Equity = 'equity',
  Crypto = 'crypto',
  Etn = 'etn',
}

export enum HistoricalPricePeriod {
  OneDay = '1D',
  OneWeek = '1W',
  OneMonth = '1M',
  ThreeMonth = '3M',
  OneYear = '1Y',
  TwoYear = '2Y',
  ThreeYear = '3Y',
  FiveYear = '5Y',
}

export enum HistoricalPriceInterval {
  OneMinute = "1m",
  ThreeMinute = "3m",
  FiveMinute = "5m",
  FifteenMinute = "15m",
  ThirtyMinute = "30m",
  OneHour = "1h",
  TwoHour = "2h",
  OneDay = "1d",
  FiveDay = "5d",
  SevenDay = "7d",
  ThirtyDay = "30d",
}

export interface Stock {
  id: string;
  assetType: AssetType;
  name: string;
  symbol: string;
  sectorId: string;
  industryId: string;
  updatedDate: string;
  dailyChange?: number;
  active: boolean;
}

export interface LocaleString {
  [key: string]: string;
}

export interface StockDetail extends Stock {
  assetClass: AssetClass;
  description: string;
  localized_description: LocaleString;
  shortDescription: string;
  localizedShortDescription: LocaleString;
  region: string;
  markets?: Market[];
}

export enum Market {
  Yildiz = "YILDIZ",
  Ana = "ANA",
  Alt = "ALT",
  YakinIzleme = "YAKIN_IZLEME",
  POIP = "POIP",
  Fon = "FON",
  Girisim = "GIRISIM",
  Emtia = "EMTIA",
}

export interface PriceDataPoint {
  d: number;
  o: number;
  uo?: number;
  h: number;
  uh?: number;
  l: number;
  ul?: number;
  c: number;
  uc?: number;
  v?: number;
  uv?: number;
}

export interface StockPriceGraph {
  symbol: string;
  '1D': PriceDataPoint[];
  '1W': PriceDataPoint[];
  '1M': PriceDataPoint[];
  '3M': PriceDataPoint[];
  '1Y': PriceDataPoint[];
  '2Y': PriceDataPoint[];
  '3Y': PriceDataPoint[];
  '5Y': PriceDataPoint[];
}

export interface StockRestriction {
  id: number;
  title: string;
  description: string;
  symbol?: string;
  startDate: string;
  endDate: string;
  market?: string;
}

export interface TickRule {
  basePrice: number;
  additionalPrice: number;
  lowerPriceLimit: number;
  upperPriceLimit: number;
  rules: TickSizeRule[];
}

export interface TickSizeRule {
  priceFrom: number;
  priceTo: number;
  tickSize: number;
}

export interface EarningsTranscriptListItem {
	symbol: string;
	year: number;
	quarter: number;
	date: string;
	fiscal_year: number;
}

export interface EarningsTranscriptWithSummary {
	symbol: string;
	year: number;
	quarter: number;
	date: string;
	content: string;
	summary?: string;
	has_summary: boolean;
}

export interface MarketState {
	id: number;
	marketSymbol?: string | null;
	state: string;
	lastTimestamp: string;
	stockSymbol?: string | null;
}

export enum ChartImagePeriod {
  OneDay = '1D',
  OneWeek = '1W',
  OneMonth = '1M',
  ThreeMonth = '3M',
  SixMonth = '6M',
  OneYear = '1Y',
  TwoYear = '2Y',
  ThreeYear = '3Y',
  FiveYear = '5Y',
  All = 'All',
}

export interface GenerateChartImageRequest {
  symbol: string;
  period?: ChartImagePeriod;
  region: Region;
  resolution?: HistoricalPriceInterval;
  indicators?: string[];
  chartType?: number;
}

export class StockClient extends Client {
  async getAllStocks(region: Region, page?: number, pageSize?: number): Promise<Stock[]> {
    return this.sendRequest<Stock[]>({
      method: 'GET',
      url: '/api/v2/stock/all',
      params: { 
        region, 
        ...(page != null ? { page } : {}),
        ...(pageSize != null ? { pageSize } : {}),
      },
    });
  }

  async getStockDetailById(id: string, locale: Locale): Promise<StockDetail> {
    return this.sendRequest<StockDetail>({
      method: 'GET',
      url: `/api/v1/stock/${id}`,
      params: { locale },
    });
  }

  async getStockDetailBySymbol(symbol: string, assetClass: AssetClass, region: Region, locale: Locale): Promise<StockDetail> {
    return this.sendRequest<StockDetail>({
      method: 'GET',
      url: '/api/v1/stock/detail',
      params: { symbol, asset_class: assetClass, region, locale },
    });
  }

  async getHistoricalPrices(symbols: string[], region: Region, keys: HistoricalPricePeriod[]): Promise<StockPriceGraph[]> {
    return this.sendRequest<StockPriceGraph[]>({
      method: 'GET',
      url: '/api/v1/stock/price',
      params: {
        symbols: symbols.join(','),
        region,
        keys: keys.join(','),
      },
    });
  }

  async getCustomHistoricalPrices(stock: string, region: Region, fromDate: string, toDate: string, interval: HistoricalPriceInterval, detail?: boolean, numIntervals?: number): Promise<PriceDataPoint[]> {
    this.validateCustomHistoricalPriceDate(fromDate);
    this.validateCustomHistoricalPriceDate(toDate);

    const params = {
      stock,
      region,
      fromDate,
      toDate,
      interval,
      ...(detail != null && { detail }),
      ...(numIntervals != null && { numIntervals })
    }

    return this.sendRequest<PriceDataPoint[]>({
      method: 'GET',
      url: '/api/v1/stock/price/interval',
      params: params,
    });
  }

  validateCustomHistoricalPriceDate(date: string) {
    const pattern =  /^\d{4}-\d{2}-\d{2}( \d{2}:\d{2}:\d{2})?$/;
    const matched = date.match(pattern);
    if (!matched) {
      throw new LaplaceHTTPError(400, "Invalid date format, allowed formats: YYYY-MM-DD, YYYY-MM-DD HH:MM:SS");
    }
  }

  async getStockRestrictions(symbol: string, region: Region): Promise<StockRestriction[]> {
    return this.sendRequest<StockRestriction[]>({
      method: 'GET',
      url: '/api/v1/stock/restrictions',
      params: { symbol, region },
    });
  }

  async getAllStockRestrictions(): Promise<StockRestriction[]> {
    return this.sendRequest<StockRestriction[]>({
      method: 'GET',
      url: '/api/v1/stock/restrictions/all'
    });
  }

  async getTickRules(symbol: string, region: Region): Promise<TickRule> {
    return this.sendRequest<TickRule>({
      method: 'GET',
      url: '/api/v1/stock/rules',
      params: { symbol, region },
    });
  }

  async getEarningsTranscripts(symbol: string, region: Region): Promise<EarningsTranscriptListItem[]> {
    return this.sendRequest<EarningsTranscriptListItem[]>({
      method: 'GET',
      url: '/api/v1/earnings/transcripts',
      params: { symbol, region },
    });
  }

  async getEarningsTranscript(symbol: string, year: number, quarter: number): Promise<EarningsTranscriptWithSummary> {
    return this.sendRequest<EarningsTranscriptWithSummary>({
      method: 'GET',
      url: '/api/v1/earnings/transcript',
      params: { symbol, year, quarter },
    });
  }

  async getStockStateAll(page: number, size: number, region: Region): Promise<PaginatedResponse<MarketState>> {
    return this.sendRequest<PaginatedResponse<MarketState>>({
      method: 'GET',
      url: '/api/v1/state/stock/all',
      params: { page, size, region },
    });
  }

  async getStockState(symbol: string): Promise<MarketState> {
    return this.sendRequest<MarketState>({
      method: 'GET',
      url: `/api/v1/state/stock/${symbol}`,
    });
  }

  async getStateAll(page: number, size: number, region: Region): Promise<PaginatedResponse<MarketState>> {
    return this.sendRequest<PaginatedResponse<MarketState>>({
      method: 'GET',
      url: '/api/v1/state/all',
      params: { page, size, region },
    });
  }

  async getState(symbol: string): Promise<MarketState> {
    return this.sendRequest<MarketState>({
      method: 'GET',
      url: `/api/v1/state/${symbol}`,
    });
  }

  async getStockChartImage(request: GenerateChartImageRequest): Promise<Blob> {
    const params: Partial<GenerateChartImageRequest> = {
      symbol: request.symbol,
      region: request.region,
    };
  
    if (request.period) params.period = request.period;
    if (request.resolution) params.resolution = request.resolution;
    if (request.indicators) params.indicators = request.indicators;
    if (request.chartType != null) params.chartType = request.chartType;

    const data = await this.sendRequest<ArrayBuffer>({
      method: "GET",
      url: "/api/v1/stock/chart",
      params,
      responseType: "arraybuffer",
    });

    return new Blob([data], { type: "image/png" });
  }
}
