const cheerio = require('cheerio');
const { hashObject } = require('./utils');

function text($root, selector) {
  return $root.find(selector).first().text().trim() || null;
}

function firstText($root, selectors) {
  for (const selector of selectors) {
    const value = text($root, selector);
    if (value) return value;
  }
  return null;
}

function texts($root, selector) {
  return $root
    .find(selector)
    .toArray()
    .map((element) => cheerio.load(element).root().text().replace(/\s+/g, ' ').trim())
    .filter(Boolean);
}

function childTexts($root, selector, childSelector) {
  const node = $root.find(selector).first();
  if (!node.length) return [];

  return node
    .children(childSelector)
    .toArray()
    .map((element) => cleanText(cheerio.load(element).root().text()))
    .filter(Boolean);
}

function isForecastRow($row) {
  return Boolean(
    $row.find('.cell-team-title a').length ||
      $row.find('.rating-mailings__favorite').length ||
      $row.find('.cell-coefficient__total').length ||
      $row.find('.cell-match').length ||
      $row.find('.cell-team-command').length
  );
}

function filterForecastRows(rows) {
  return rows.filter((_, element) => isForecastRow(cheerio.load(element).root()));
}

function resolveRows($scope, selectors, activeTab = null) {
  if (activeTab === 1) {
    const activeRows = filterForecastRows($scope.find('#tab-forecast-active .rTableBody .table-link'));
    if (activeRows.length > 0) {
      return activeRows;
    }
  }

  if (activeTab === 2) {
    const inactiveRows = filterForecastRows($scope.find('#tab-forecast-zip .rTableBody .table-link'));
    if (inactiveRows.length > 0) {
      return inactiveRows;
    }
  }

  const forecastRows = $scope
    .find('#tab-forecast-active .rTableBody .table-link')
    .add($scope.find('#tab-forecast-zip .rTableBody .table-link'));

  const filteredForecastRows = filterForecastRows(forecastRows);
  if (filteredForecastRows.length > 0) {
    return filteredForecastRows;
  }

  const tableRows = $scope.find('.rTableBody .table-link');
  const filteredTableRows = filterForecastRows(tableRows);
  if (filteredTableRows.length > 0) {
    return filteredTableRows;
  }

  return filterForecastRows($scope.find(selectors.item));
}

function attrOrText($root, selector) {
  const node = $root.find(selector).first();
  if (!node.length) return null;
  return node.attr('data-id') || node.attr('id') || node.text().trim() || null;
}

function cleanText(value) {
  if (value == null) return null;
  const normalized = String(value).replace(/\s+/g, ' ').trim();
  return normalized || null;
}

function attr($root, selector, name) {
  const node = $root.find(selector).first();
  if (!node.length) return null;
  return node.attr(name) || null;
}

function attrs($root, selector, name) {
  return $root
    .find(selector)
    .toArray()
    .map((element) => cleanText(element.attribs?.[name]))
    .filter(Boolean);
}

function resolveForecastContainer($row) {
  let cursor = $row;
  while (cursor && cursor.length) {
    const idAttr = cursor.attr('id') || '';
    const classAttr = cursor.attr('class') || '';
    const classList = classAttr.split(/\s+/).filter(Boolean);

    if (idAttr === 'tab-forecast-active' || classList.includes('tab-forecast-active')) {
      return { container: 'tab-forecast-active', activeTab: 1, forecastInactive: false };
    }

    if (idAttr === 'tab-forecast-zip' || classList.includes('tab-forecast-zip')) {
      return { container: 'tab-forecast-zip', activeTab: 2, forecastInactive: true };
    }

    cursor = cursor.parent();
  }

  return { container: null, activeTab: null, forecastInactive: null };
}

function extractStructuredItem($row, selectors) {
  const teamNames = texts($row, '.cell-team-command');
  const rateParts = childTexts($row, '.cell-rate', 'span');
  const timeEvent = cleanText($row.find('.time-event').first().text());
  const titleText = cleanText(
    $row
      .find('.cell-team-title')
      .clone()
      .find('.info-help')
      .remove()
      .end()
      .text()
  );
  const score = cleanText(text($row, '.cell-team-score'));
  const publicationType = cleanText(text($row, '.cell-type .type-live, .cell-type .type-prematch, .cell-type [class*="type-"]'));
  const publicationAge = cleanText(text($row, '.cell-type .time-since'));
  const publicationTimer = cleanText(attr($row, '.cell-type .js-published-live-date, .cell-type .time-since', 'data-timer'));
  const buyButtonText = cleanText($row.find('.btn_buy, .popupTarget').first().text());
  const coefficientText = cleanText(text($row, '.cell-coefficient'));
  const parentContext = resolveForecastContainer($row);
  const inactiveHint = attrs($row, '[data-tippy-content]', 'data-tippy-content').find((value) =>
    value.includes('Прогноз неактивен') && value.includes('у букмекера больше нет события')
  );
  const inactiveByParent = parentContext.forecastInactive === true;

  return {
    id: attr($row, '.rating-mailings__favorite', 'data-model-id') || attrOrText($row, selectors.itemId),
    title: teamNames.length > 0 ? teamNames.join(' vs ') : titleText || text($row, selectors.title),
    status: timeEvent || text($row, selectors.status),
    coef:
      cleanText(
        firstText($row, [
          '.cell-coefficient__total',
          '.completed_rate_desc .rate',
          '.cell-prognos .rate',
          selectors.coef
        ])
      ),
    date: cleanText(text($row, '.time-event:not(.time-event_live)')) || text($row, selectors.date),
    sport: attr($row, '.cell-icon.js-sport-tooltip', 'data-tippy-content'),
    eventTime: timeEvent,
    score,
    homeTeam: teamNames[0] || null,
    awayTeam: teamNames[1] || null,
    tournament: cleanText(text($row, '.cell-team-tnm')),
    eventUrl: attr($row, '.cell-team-title a', 'href'),
    publicationType,
    publicationAge,
    publicationTimer,
    buyButtonText,
    coefficientText,
    forecastInactive: inactiveByParent || Boolean(inactiveHint),
    activeTab: parentContext.activeTab,
    forecastContainer: parentContext.container,
    stake: rateParts[0] || null,
    stakePercent: rateParts[1] || null,
    selectionText: cleanText(firstText($row, ['.rate-description', '.cell-prognos .rate-description'])),
    images: attrs($row, '.cell-prognos img[data-src]', 'data-src'),
    rawText: cleanText($row.text())
  };
}

function shouldSkipStructuredItem(item) {
  const normalizedTitle = String(item.title || '').toLowerCase();
  const normalizedCoefficientText = String(item.coefficientText || '').toLowerCase();
  const normalizedBuyButtonText = String(item.buyButtonText || '').toLowerCase();

  const hasTeams = Boolean(item.homeTeam && item.awayTeam);
  const hasOpenEvent = Boolean(item.eventUrl);
  const hasSelection = Boolean(item.selectionText);
  const hasOdds = Boolean(item.coef);
  const hasHiddenMarker =
    normalizedTitle.includes('скрыто') ||
    normalizedCoefficientText.includes('скрыт') ||
    normalizedBuyButtonText.includes('купить');

  if (hasHiddenMarker && !hasTeams && !hasOpenEvent && !hasSelection && !hasOdds) {
    return true;
  }

  return false;
}

function normalizeItem(item) {
  const cleaned = {};
  for (const [key, value] of Object.entries(item)) {
    if (typeof value === 'string') {
      cleaned[key] = value.replace(/\s+/g, ' ').trim();
      continue;
    }

    if (Array.isArray(value)) {
      cleaned[key] = value.map((entry) => (typeof entry === 'string' ? entry.replace(/\s+/g, ' ').trim() : entry));
      continue;
    }

    cleaned[key] = value;
  }

  const keyBase = [cleaned.id, cleaned.title, cleaned.status, cleaned.coef, cleaned.date, cleaned.score]
    .filter(Boolean)
    .join('|');
  cleaned.__key = hashObject(keyBase || cleaned);
  return cleaned;
}

function parseItemsFromHtml(html, selectors, activeTab = null) {
  const $ = cheerio.load(html);
  const items = [];
  const $scope = $('#pjax-forecast-list').length ? $('#pjax-forecast-list') : $.root();
  const $rows = resolveRows($scope, selectors, activeTab);

  $rows.each((_, element) => {
    const $row = $(element);
    const structuredItem = extractStructuredItem($row, selectors);

    if (shouldSkipStructuredItem(structuredItem)) {
      return;
    }

    const hasMeaningfulContent =
      structuredItem.homeTeam ||
      structuredItem.awayTeam ||
      structuredItem.title ||
      structuredItem.coef ||
      structuredItem.eventUrl ||
      structuredItem.stake ||
      structuredItem.publicationType;

    if (!hasMeaningfulContent) {
      return;
    }

    const item = normalizeItem(structuredItem);
    items.push(item);
  });

  items.sort((a, b) => a.__key.localeCompare(b.__key));
  return items;
}

function countForecastRows(html, selectors, activeTab = null) {
  const $ = cheerio.load(html);
  const $scope = $('#pjax-forecast-list').length ? $('#pjax-forecast-list') : $.root();
  const $rows = resolveRows($scope, selectors, activeTab);
  return $rows.length;
}

module.exports = {
  parseItemsFromHtml,
  countForecastRows
};
