import * as cheerio from 'cheerio';
import { createClient } from '@supabase/supabase-js';
import type { Database } from './database.types';
import { logSecurityEvent, logError } from './logger';
import fs from 'fs/promises';
import path from 'path';

// Environment variables
const supabaseUrl = process.env.SUPABASE_URL || import.meta.env.SUPABASE_URL || 'https://zctjaivtfyfxokfaemek.supabase.co';
const supabaseServiceKey = process.env.SUPABASE_SERVICE_KEY || import.meta.env.SUPABASE_SERVICE_KEY || '';

// Configuration
const REDIRECT_URL = 'https://blackcanyontickets.com/events';
const BASE_URL = 'https://blackcanyontickets.com';
const LAST_SLUG_FILE = path.join(process.cwd(), 'logs', 'last_scraped_slug.txt');
const SCRAPER_ORGANIZATION_ID = process.env.SCRAPER_ORGANIZATION_ID || 'scraped-events-org';

// Create Supabase client with proper types
let supabase: ReturnType<typeof createClient<Database>> | null = null;

try {
  if (supabaseUrl && supabaseServiceKey) {
    supabase = createClient<Database>(supabaseUrl, supabaseServiceKey);
  }
} catch (error) {
  logError('Failed to initialize Supabase client for scraper', error);
}

interface ScrapedEventDetails {
  slug: string;
  title: string;
  description?: string;
  venue?: string;
  startTime?: string;
  endTime?: string;
  imageUrl?: string;
  category?: string;
}

/**
 * Get the current event slug by following the redirect from /events
 */
async function getCurrentEventSlug(): Promise<string | null> {
  try {
    const response = await fetch(REDIRECT_URL, { 
      redirect: 'manual',
      headers: {
        'User-Agent': 'Mozilla/5.0 (compatible; BCT-Event-Scraper/1.0)'
      }
    });
    
    if (response.status === 302 || response.status === 301) {
      const location = response.headers.get('location');
      if (location) {
        // Extract slug from the redirect URL
        const url = new URL(location, BASE_URL);
        return url.pathname;
      }
    }
    
    return null;
  } catch (error) {
    logError('Failed to get current event slug', error);
    return null;
  }
}

/**
 * Fetch and parse event details from the event page
 */
async function fetchEventDetails(slug: string): Promise<ScrapedEventDetails | null> {
  try {
    const eventUrl = `${BASE_URL}${slug}`;
    const response = await fetch(eventUrl, {
      headers: {
        'User-Agent': 'Mozilla/5.0 (compatible; BCT-Event-Scraper/1.0)'
      }
    });
    
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
    }
    
    const html = await response.text();
    const $ = cheerio.load(html);
    
    // Extract event details - these selectors may need adjustment based on actual HTML structure
    const title = $('h1').first().text().trim() || 
                  $('[data-event-title]').text().trim() ||
                  $('title').text().trim().split(' - ')[0];
    
    const description = $('[data-event-description]').text().trim() ||
                       $('.event-description').text().trim() ||
                       $('meta[name="description"]').attr('content') ||
                       '';
    
    const venue = $('[data-event-venue]').text().trim() ||
                  $('.venue-name').text().trim() ||
                  $('.event-venue').text().trim() ||
                  'Black Canyon Tickets Venue';
    
    // Try to extract date/time information
    const dateTimeText = $('[data-event-date]').text().trim() ||
                        $('[data-event-time]').text().trim() ||
                        $('.event-date').text().trim() ||
                        $('.event-time').text().trim();
    
    // Try to extract image
    const imageUrl = $('[data-event-image]').attr('src') ||
                    $('.event-image img').attr('src') ||
                    $('meta[property="og:image"]').attr('content') ||
                    $('img[alt*="event" i]').first().attr('src');
    
    // Determine category based on content
    const category = determineCategoryFromContent($, title, description);
    
    // Parse dates if available
    const { startTime, endTime } = parseDateTimeFromContent(dateTimeText, $);
    
    return {
      slug,
      title: title || 'Featured Event',
      description: description.length > 0 ? description.substring(0, 500) : undefined,
      venue,
      startTime,
      endTime,
      imageUrl: imageUrl ? new URL(imageUrl, BASE_URL).toString() : undefined,
      category
    };
    
  } catch (error) {
    logError(`Failed to fetch event details for ${slug}`, error);
    return null;
  }
}

/**
 * Determine event category based on content analysis
 */
function determineCategoryFromContent($: cheerio.CheerioAPI, title: string, description: string): string {
  const content = (title + ' ' + description).toLowerCase();
  
  // Define category keywords
  const categoryKeywords = {
    music: ['concert', 'music', 'band', 'performance', 'singer', 'acoustic', 'jazz', 'classical', 'rock', 'pop'],
    arts: ['art', 'gallery', 'exhibition', 'theater', 'theatre', 'play', 'drama', 'dance', 'ballet'],
    community: ['community', 'festival', 'fair', 'celebration', 'parade', 'market', 'fundraiser', 'charity'],
    business: ['business', 'networking', 'conference', 'seminar', 'workshop', 'meetup', 'corporate'],
    food: ['food', 'wine', 'tasting', 'dinner', 'restaurant', 'culinary', 'chef', 'cooking'],
    sports: ['sports', 'race', 'marathon', 'golf', 'tournament', 'athletic', 'competition', 'game']
  };
  
  // Find the category with the most matches
  let bestCategory = 'community';
  let maxMatches = 0;
  
  for (const [category, keywords] of Object.entries(categoryKeywords)) {
    const matches = keywords.filter(keyword => content.includes(keyword)).length;
    if (matches > maxMatches) {
      maxMatches = matches;
      bestCategory = category;
    }
  }
  
  return bestCategory;
}

/**
 * Parse date/time information from content
 */
function parseDateTimeFromContent(dateTimeText: string, $: cheerio.CheerioAPI): { startTime?: string; endTime?: string } {
  if (!dateTimeText) {
    // Default to a future date if no date found
    const futureDate = new Date();
    futureDate.setDate(futureDate.getDate() + 30); // 30 days from now
    return {
      startTime: futureDate.toISOString()
    };
  }
  
  try {
    // Try to parse the date/time
    // This is a simplified parser - could be enhanced based on actual format
    const date = new Date(dateTimeText);
    if (!isNaN(date.getTime())) {
      return {
        startTime: date.toISOString()
      };
    }
  } catch (error) {
    // Ignore parsing errors
  }
  
  // Fallback to future date
  const futureDate = new Date();
  futureDate.setDate(futureDate.getDate() + 30);
  return {
    startTime: futureDate.toISOString()
  };
}

/**
 * Load the last seen slug from file
 */
async function loadLastSeenSlug(): Promise<string | null> {
  try {
    return await fs.readFile(LAST_SLUG_FILE, 'utf-8');
  } catch (error) {
    // File doesn't exist or can't be read
    return null;
  }
}

/**
 * Save the last seen slug to file
 */
async function saveLastSeenSlug(slug: string): Promise<void> {
  try {
    // Ensure logs directory exists
    await fs.mkdir(path.dirname(LAST_SLUG_FILE), { recursive: true });
    await fs.writeFile(LAST_SLUG_FILE, slug);
  } catch (error) {
    logError('Failed to save last seen slug', error);
  }
}

/**
 * Add scraped event to the database as a featured event
 */
async function addScrapedEventToDatabase(eventDetails: ScrapedEventDetails): Promise<boolean> {
  if (!supabase) {
    logError('Supabase client not available for adding scraped event');
    return false;
  }
  
  try {
    // Create a deterministic ID based on the slug to avoid duplicates
    const eventId = `scraped-${eventDetails.slug.replace(/[^a-zA-Z0-9]/g, '-')}`;
    
    // Check if event already exists
    const { data: existingEvent } = await supabase
      .from('events')
      .select('id')
      .eq('id', eventId)
      .single();
    
    if (existingEvent) {

      return true;
    }
    
    // Insert the new event as featured and public
    const { error } = await supabase
      .from('events')
      .insert({
        id: eventId,
        title: eventDetails.title,
        slug: `external-${eventDetails.slug.split('/').pop()}` || eventId,
        description: eventDetails.description,
        venue: eventDetails.venue || 'Black Canyon Tickets Venue',
        start_time: eventDetails.startTime || new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(),
        end_time: eventDetails.endTime,
        image_url: eventDetails.imageUrl,
        category: eventDetails.category,
        is_featured: true,
        is_public: true,
        is_published: true,
        external_source: 'scraper',
        organization_id: SCRAPER_ORGANIZATION_ID,
        created_by: SCRAPER_ORGANIZATION_ID // This will need to be a valid user ID
      });
    
    if (error) {
      logError('Failed to insert scraped event into database', error);
      return false;
    }

    return true;
    
  } catch (error) {
    logError('Error adding scraped event to database', error);
    return false;
  }
}

/**
 * Main scraper function - detects new events and adds them as featured
 */
export async function runEventScraper(): Promise<{ success: boolean; message: string; newEvent?: ScrapedEventDetails }> {
  try {

    // Get current event slug
    const currentSlug = await getCurrentEventSlug();
    if (!currentSlug) {
      return {
        success: true,
        message: 'No event redirect found on blackcanyontickets.com/events'
      };
    }

    // Check if this is a new event
    const lastSeenSlug = await loadLastSeenSlug();
    if (currentSlug === lastSeenSlug) {
      return {
        success: true,
        message: 'No new event detected (same as last seen)'
      };
    }
    
    // Fetch event details
    const eventDetails = await fetchEventDetails(currentSlug);
    if (!eventDetails) {
      return {
        success: false,
        message: `Failed to extract event details from ${currentSlug}`
      };
    }

    // Add to database as featured event
    const added = await addScrapedEventToDatabase(eventDetails);
    if (!added) {
      return {
        success: false,
        message: 'Failed to add event to database'
      };
    }
    
    // Save the current slug as last seen
    await saveLastSeenSlug(currentSlug);
    
    // Log the successful scraping
    logSecurityEvent({
      type: 'scraper_success',
      severity: 'info',
      details: {
        slug: currentSlug,
        title: eventDetails.title,
        venue: eventDetails.venue,
        category: eventDetails.category
      }
    });
    
    return {
      success: true,
      message: `Successfully scraped and added featured event: ${eventDetails.title}`,
      newEvent: eventDetails
    };
    
  } catch (error) {
    logError('Event scraper failed', error);
    
    logSecurityEvent({
      type: 'scraper_error',
      severity: 'high',
      details: { error: error instanceof Error ? error.message : 'Unknown error' }
    });
    
    return {
      success: false,
      message: 'Event scraper encountered an error'
    };
  }
}

/**
 * Initialize scraper organization if it doesn't exist
 */
export async function initializeScraperOrganization(): Promise<boolean> {
  if (!supabase) {
    return false;
  }
  
  try {
    // Check if scraper organization exists
    const { data: existingOrg } = await supabase
      .from('organizations')
      .select('id')
      .eq('id', SCRAPER_ORGANIZATION_ID)
      .single();
    
    if (existingOrg) {
      return true;
    }
    
    // Create scraper organization
    const { error: orgError } = await supabase
      .from('organizations')
      .insert({
        id: SCRAPER_ORGANIZATION_ID,
        name: 'Black Canyon Tickets - Scraped Events',
        logo: null,
        stripe_account_id: null
      });
    
    if (orgError) {
      logError('Failed to create scraper organization', orgError);
      return false;
    }
    
    // Create scraper user
    const { error: userError } = await supabase
      .from('users')
      .insert({
        id: SCRAPER_ORGANIZATION_ID,
        email: 'scraper@blackcanyontickets.com',
        name: 'Event Scraper',
        organization_id: SCRAPER_ORGANIZATION_ID
      });
    
    if (userError) {
      logError('Failed to create scraper user', userError);
      return false;
    }

    return true;
    
  } catch (error) {
    logError('Failed to initialize scraper organization', error);
    return false;
  }
}