import { TE_CONFIG } from '../shared/config';
import { decodeTEData } from '../shared/decode';
import type { TeCdnResponse, TeDataFetchPayload } from '../shared/types';
import type { TeHandler } from '../te.handler';

export async function dataFetch(
  this: TeHandler,
  payload: TeDataFetchPayload,
): Promise<{ success: boolean; recordCount: number }> {
  const { logger, mongodb, http, proxy } = this;
  const { teSymbol, url, isInitialFetch } = payload;

  const teDoc = await mongodb?.findOne('teUrls', { url });
  if (!teDoc?.teChartsDatasource || !teDoc?.teChartToken || !teDoc?.teChart || !teDoc?.teLastUpdate) {
    logger.error(`Missing CDN fields for ${teSymbol} (${url})`);
    return { success: false, recordCount: 0 };
  }

  const pathSegment = TE_CONFIG.CDN_PATH_MAP[teDoc.teChart];
  if (!pathSegment) {
    logger.warn(`Unknown teChart type: ${teDoc.teChart} for ${teSymbol}`);
    return { success: false, recordCount: 0 };
  }

  // Build CDN URL
  const cdnUrl = new URL(`${teDoc.teChartsDatasource}/${pathSegment}/${encodeURIComponent(teSymbol.toLowerCase())}`);
  if (isInitialFetch) {
    cdnUrl.searchParams.set('span', 'max');
  }
  cdnUrl.searchParams.set('v', teDoc.teLastUpdate);
  cdnUrl.searchParams.set('key', teDoc.teChartToken);

  logger.info(`Fetching TE data: ${teSymbol} (${isInitialFetch ? 'initial' : 'update'})`);

  const response = await http.get(cdnUrl.toString(), {
    proxy: proxy?.getProxy(),
    headers: { 'Accept': '*/*', 'User-Agent': TE_CONFIG.USER_AGENT },
  });

  if (!response.ok) {
    logger.error(`CDN ${response.status} for ${teSymbol}`);
    throw new Error(`CDN HTTP ${response.status} for ${teSymbol}`);
  }

  const rawText = (await response.text()).replaceAll('"', '');
  if (!rawText.length) {
    logger.warn(`Empty response for ${teSymbol}`);
    return { success: false, recordCount: 0 };
  }

  // Decode: response is an array wrapping the actual object
  const decoded = decodeTEData<TeCdnResponse[]>(rawText, TE_CONFIG.KEY);
  const serie = decoded?.[0]?.series?.[0]?.serie;

  if (!serie?.data?.length) {
    logger.warn(`No data points decoded for ${teSymbol}`);
    await mongodb?.updateOne('teUrls', { url }, {
      $set: {
        lastDataFetch: new Date(),
        lastDataFetchUpdate: teDoc.teLastUpdate,
        lastDataFetchCount: 0,
      },
    });
    return { success: true, recordCount: 0 };
  }

  // Transform data points: [value, epoch, null, "YYYY-MM-DD"] → { d, v }
  const dataPoints = serie.data.map(([value, _epoch, _null, date]) => ({ d: date, v: value }));
  const forecastPoints = serie.forecast?.map(([value, _epoch, _null, date]) => ({ d: date, v: value })) ?? [];

  logger.info(`Storing ${dataPoints.length} points + ${forecastPoints.length} forecast for ${teSymbol}`);

  if (isInitialFetch) {
    // Initial: upsert the full document
    await mongodb?.updateOne('teData', { teSymbol }, {
      $set: {
        teSymbol,
        name: serie.name,
        unit: serie.unit,
        source: serie.source,
        frequency: serie.frequency,
        country: serie.country,
        category: serie.category,
        data: dataPoints,
        forecast: forecastPoints,
        lastUpdated: new Date(),
      },
      $setOnInsert: { createdAt: new Date() },
    }, { upsert: true });
  } else {
    // Update: merge new points into existing data array by date
    const existing = await mongodb?.findOne('teData', { teSymbol });
    if (existing?.data) {
      const dateMap = new Map(existing.data.map((p: any) => [p.d, p.v]));
      for (const p of dataPoints) {
        dateMap.set(p.d, p.v);
      }
      const merged = Array.from(dateMap.entries())
        .sort(([a], [b]) => a.localeCompare(b))
        .map(([d, v]) => ({ d, v }));

      await mongodb?.updateOne('teData', { teSymbol }, {
        $set: {
          data: merged,
          forecast: forecastPoints,
          lastUpdated: new Date(),
        },
      });
    } else {
      // No existing doc (shouldn't happen but handle gracefully)
      await mongodb?.updateOne('teData', { teSymbol }, {
        $set: {
          teSymbol,
          name: serie.name,
          unit: serie.unit,
          source: serie.source,
          frequency: serie.frequency,
          country: serie.country,
          category: serie.category,
          data: dataPoints,
          forecast: forecastPoints,
          lastUpdated: new Date(),
        },
        $setOnInsert: { createdAt: new Date() },
      }, { upsert: true });
    }
  }

  const totalPoints = dataPoints.length + forecastPoints.length;

  await mongodb?.updateOne('teUrls', { url }, {
    $set: {
      lastDataFetch: new Date(),
      lastDataFetchUpdate: teDoc.teLastUpdate,
      lastDataFetchCount: totalPoints,
    },
  });

  return { success: true, recordCount: totalPoints };
}
