import { Lead, OutputConfig } from '../types';
import { ab } from './ab';
import { randomDelay, humanize, getDailyStats, saveDailyStats, shuffle, dismissBanners } from './anti-detection';
import { extractBuilding } from './extractors/building';
import { extractOwners } from './extractors/owner';
import { extractSales } from './extractors/sales';
import { extractDebt } from './extractors/debt';
import { extractTax } from './extractors/tax';

interface PropertyRef {
  name: string;
  ref: string;
  _useCurrentPage?: boolean;
}

/**
 * Orchestrate extraction for a list of properties from a search.
 * Only runs the extractors requested by OutputConfig.
 */
export async function extractFromSearch(
  searchId: string,
  outputConfig: OutputConfig,
  maxProperties: number,
  onProgress?: (processed: number, total: number, leads: number) => void
): Promise<Lead[]> {
  const leads: Lead[] = [];
  const dailyStats = getDailyStats();

  // Navigate to search
  const searchUrl = `https://app.reonomy.com/#!/search/${searchId}`;
  ab(`open "${searchUrl}"`);
  await randomDelay(6000, 10000);

  // Check if we got redirected to login
  let urlCheck = ab('eval "window.location.href"');
  if (urlCheck.output?.includes('auth.reonomy.com') || urlCheck.output?.includes('login')) {
    throw new Error('Not authenticated — need to login first');
  }

  // Dismiss banners
  await dismissBanners();

  // Get property list
  const properties = await getPropertyList(searchId, maxProperties);
  if (properties.length === 0) {
    throw new Error('No properties found in search results');
  }

  // Shuffle for anti-detection
  const shuffledProps = shuffle(properties).slice(0, maxProperties);

  // Process each property
  for (let i = 0; i < shuffledProps.length; i++) {
    const prop = shuffledProps[i];
    onProgress?.(i, shuffledProps.length, leads.length);

    await humanize();

    try {
      // Navigate to property
      if (!prop._useCurrentPage) {
        let clickResult = ab(`click @${prop.ref}`);
        if (!clickResult.success) {
          // Retry with fresh snapshot
          await randomDelay(1000, 2000);
          const freshSnap = ab('snapshot -i');
          const escaped = prop.name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').substring(0, 30);
          const freshMatch = freshSnap.output?.match(
            new RegExp(`(?:button|heading) "${escaped}[^"]*" \\[ref=(e\\d+)\\]`)
          );
          if (freshMatch) {
            ab(`click @${freshMatch[1]}`);
          } else {
            dailyStats.propertiesScraped++;
            continue;
          }
        }
        await randomDelay(5000, 8000);
      }

      // Get property ID from URL
      const propUrl = ab('eval "window.location.href"');
      const propIdMatch = propUrl.output?.match(/property\/([a-f0-9-]+)/);
      const propertyId = propIdMatch ? propIdMatch[1] : 'unknown';

      // Get address from heading
      let propertyAddress = prop.name;
      const titleSnap = ab('snapshot');
      const headingMatch = titleSnap.output?.match(/heading "([^"]+)"/);
      if (headingMatch && headingMatch[1] !== 'Owners' && headingMatch[1] !== 'Owner' && headingMatch[1].length > 5) {
        propertyAddress = headingMatch[1];
      }

      // Build lead data based on OutputConfig
      const lead: Lead = {
        jobId: '', // Will be set by the queue
        scrapeDate: new Date().toISOString(),
        propertyId,
        propertyAddress,
      };

      // Run requested extractors
      const needsBuilding = outputConfig.propertyInfo && outputConfig.propertyInfo.length > 0;
      const needsOwner = (outputConfig.ownerInfo && outputConfig.ownerInfo.length > 0) ||
                         (outputConfig.contactInfo && outputConfig.contactInfo.length > 0);
      const needsSales = outputConfig.salesInfo && outputConfig.salesInfo.length > 0;
      const needsDebt = outputConfig.debtInfo && outputConfig.debtInfo.length > 0;
      const needsTax = outputConfig.taxInfo && outputConfig.taxInfo.length > 0;

      // Building & Lot is the default tab, extract first
      if (needsBuilding) {
        lead.building = await extractBuilding();
      }

      // Owner tab
      if (needsOwner) {
        lead.owners = await extractOwners();
      }

      // Sales tab
      if (needsSales) {
        lead.sales = await extractSales();
      }

      // Debt tab
      if (needsDebt) {
        lead.debt = await extractDebt();
      }

      // Tax tab
      if (needsTax) {
        lead.tax = await extractTax();
      }

      leads.push(lead);
      dailyStats.leadsFound++;
      dailyStats.propertiesScraped++;

      // Navigate to next property or back to search
      const nextSnap = ab('snapshot -i', { verbose: false });
      const nextMatch = nextSnap.output?.match(/button "Next property" \[ref=(e\d+)\]/);
      if (nextMatch && i < shuffledProps.length - 1) {
        ab(`click @${nextMatch[1]}`);
        await randomDelay(5000, 8000);
        shuffledProps[i + 1]._useCurrentPage = true;
      } else {
        ab(`open "${searchUrl}"`);
        await randomDelay(6000, 10000);
        await dismissBanners();
      }

      // Occasional longer break
      if (Math.random() < 0.2) {
        await randomDelay(8000, 15000);
      }

    } catch (propError: any) {
      // On error, navigate back and continue
      ab(`open "${searchUrl}"`);
      await randomDelay(5000, 8000);
      dailyStats.propertiesScraped++;
    }

    saveDailyStats(dailyStats);
  }

  onProgress?.(shuffledProps.length, shuffledProps.length, leads.length);
  return leads;
}

/**
 * Get the list of properties from search results page
 */
async function getPropertyList(searchId: string, maxProperties: number): Promise<PropertyRef[]> {
  const snap = ab('snapshot -i');
  const properties: PropertyRef[] = [];

  const allMatches = snap.output?.matchAll(/(?:button|heading) "([^"]+)" \[ref=(e\d+)\]/g) || [];
  for (const match of allMatches) {
    const text = match[1];
    // Skip non-property elements
    if (
      text.includes('Saved Searches') || text.includes('Help Center') ||
      text.includes('More filters') || text.includes('View More') ||
      text.includes("Let's Go") || text.includes('Learn about') ||
      text.includes('properties') || text.length < 10
    ) {
      continue;
    }
    // Match addresses
    if (/\d+.*(?:st|ave|blvd|dr|ln|rd|way|ct|highway|pl|cir)/i.test(text)) {
      properties.push({ name: text.substring(0, 60), ref: match[2] });
    }
  }

  // Click "View More" if available and we need more
  if (properties.length < maxProperties) {
    const viewMoreMatch = snap.output?.match(/button "View More" \[ref=(e\d+)\]/);
    if (viewMoreMatch) {
      ab(`click @${viewMoreMatch[1]}`);
      await randomDelay(3000, 5000);

      const moreSnap = ab('snapshot -i');
      const moreMatches = moreSnap.output?.matchAll(/(?:button|heading) "([^"]+)" \[ref=(e\d+)\]/g) || [];
      for (const match of moreMatches) {
        const text = match[1];
        if (
          text.includes('Saved Searches') || text.includes('Help Center') ||
          text.includes('More filters') || text.includes('View More') ||
          text.includes("Let's Go") || text.includes('Learn about') ||
          text.includes('properties') || text.length < 10
        ) continue;
        if (/\d+.*(?:st|ave|blvd|dr|ln|rd|way|ct|highway|pl|cir)/i.test(text)) {
          const existing = properties.find((p) => p.name === text.substring(0, 60));
          if (!existing) {
            properties.push({ name: text.substring(0, 60), ref: match[2] });
          }
        }
      }
    }
  }

  return properties;
}
