import { Page } from 'playwright';
import fs from 'fs';
import path from 'path';
import levenshtein from 'js-levenshtein';

const fallbackPath = path.join(__dirname, 'fallback-selectors.json');

function loadFallbacks(): Record<string, string> {
  return fs.existsSync(fallbackPath)
    ? JSON.parse(fs.readFileSync(fallbackPath, 'utf-8'))
    : {};
}

function saveFallback(original: string, healed: string) {
  const data = loadFallbacks();
  data[original] = healed;
  fs.writeFileSync(fallbackPath, JSON.stringify(data, null, 2));
}

async function findHealedSelector(
  page: Page,
  selector: string
): Promise<string | null> {
  const failedText = await page
    .locator(selector)
    .textContent()
    .catch(() => '');
  if (!failedText) return null;

  const elements = await page.$$('body *');
  let best = { selector: '', score: Infinity };

  for (const el of elements) {
    const text = await el.textContent();
    if (!text) continue;

    const score = levenshtein(failedText.trim(), text.trim());
    if (score < best.score && score < 10) {
      const tag = await el.evaluate((e) => e.tagName.toLowerCase());
      best = { selector: `${tag}:has-text("${text.trim()}")`, score };
    }
  }

  if (best.selector) {
    saveFallback(selector, best.selector);
    return best.selector;
  }

  return null;
}

export async function patchPageGlobally(page: Page) {
  const actions = [
    'click',
    'fill',
    'check',
    'uncheck',
    'hover',
    'dblclick',
  ];
  const fallbacks = loadFallbacks();

  for (const method of actions) {
    const original = (page as any)[method].bind(page);

    (page as any)[method] = async (
      selector: string,
      ...args: any[]
    ) => {
      try {
        return await original(selector, ...args);
      } catch (err) {
        console.warn(`[Healenium] ${method} failed on: ${selector}`);

        const fallbacks = loadFallbacks();
        if (fallbacks[selector]) {
          console.log(
            `[Healenium] Retrying with fallback: ${fallbacks[selector]}`
          );
          return await original(fallbacks[selector], ...args);
        }

        const healed = await findHealedSelector(page, selector);
        if (healed) {
          console.log(
            `[Healenium] Found and saved fallback: ${healed}`
          );
          saveFallback(selector, healed);
          return await original(healed, ...args);
        }

        throw err;
      }
    };
  }
}
