use chrono::Utc;
use sqlx::{PgPool, Row};
use std::env;
use tokio;
use church_api::utils::sanitize::strip_html_tags;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("🧹 Church API - HTML Entity Cleaning Tool");
    println!("==========================================");
    println!();

    // Get database URL
    let database_url = env::var("DATABASE_URL")
        .unwrap_or_else(|_| "postgresql://localhost/church_api".to_string());

    // Connect to database
    println!("📡 Connecting to database...");
    let pool = PgPool::connect(&database_url).await?;
    println!("✅ Connected successfully!");
    println!();

    // Check for dirty data first
    println!("🔍 Analyzing database for HTML entities...");
    let dirty_count = count_dirty_records(&pool).await?;
    println!("📊 Found {} records with HTML tags or entities", dirty_count);
    
    if dirty_count == 0 {
        println!("✅ Database is already clean! No migration needed.");
        return Ok(());
    }

    println!();
    println!("🚀 Starting HTML entity cleanup...");
    println!();

    let start_time = Utc::now();
    let mut total_cleaned = 0;

    // Clean each table
    total_cleaned += clean_bulletins(&pool).await?;
    total_cleaned += clean_events(&pool).await?;
    total_cleaned += clean_pending_events(&pool).await?;
    total_cleaned += clean_members(&pool).await?;
    total_cleaned += clean_church_config(&pool).await?;
    total_cleaned += clean_users(&pool).await?;
    
    // Optional tables (might not exist)
    total_cleaned += clean_media_items(&pool).await.unwrap_or(0);
    total_cleaned += clean_transcoded_media(&pool).await.unwrap_or(0);

    let duration = Utc::now() - start_time;
    
    println!();
    println!("🎉 Cleanup completed!");
    println!("📊 Total records cleaned: {}", total_cleaned);
    println!("⏱️  Duration: {}ms", duration.num_milliseconds());
    
    // Final verification
    println!();
    println!("🔍 Verifying cleanup...");
    let remaining_dirty = count_dirty_records(&pool).await?;
    
    if remaining_dirty == 0 {
        println!("✅ Success! No HTML entities remaining in database.");
    } else {
        println!("⚠️  Warning: {} records still contain HTML entities", remaining_dirty);
    }

    pool.close().await;
    Ok(())
}

async fn count_dirty_records(pool: &PgPool) -> Result<i64, sqlx::Error> {
    let count = sqlx::query(r#"
        SELECT COUNT(*) as count FROM (
            SELECT id FROM bulletins WHERE 
                title ~ '<[^>]*>' OR sabbath_school ~ '<[^>]*>' OR divine_worship ~ '<[^>]*>' OR 
                scripture_reading ~ '<[^>]*>' OR sunset ~ '<[^>]*>' OR
                title ~ '&(nbsp|amp|lt|gt|quot|#39);' OR sabbath_school ~ '&(nbsp|amp|lt|gt|quot|#39);' OR 
                divine_worship ~ '&(nbsp|amp|lt|gt|quot|#39);' OR scripture_reading ~ '&(nbsp|amp|lt|gt|quot|#39);' OR 
                sunset ~ '&(nbsp|amp|lt|gt|quot|#39);'
            UNION ALL
            SELECT id FROM events WHERE 
                title ~ '<[^>]*>' OR description ~ '<[^>]*>' OR location ~ '<[^>]*>' OR 
                location_url ~ '<[^>]*>' OR approved_from ~ '<[^>]*>' OR
                title ~ '&(nbsp|amp|lt|gt|quot|#39);' OR description ~ '&(nbsp|amp|lt|gt|quot|#39);' OR 
                location ~ '&(nbsp|amp|lt|gt|quot|#39);' OR location_url ~ '&(nbsp|amp|lt|gt|quot|#39);' OR 
                approved_from ~ '&(nbsp|amp|lt|gt|quot|#39);'
            UNION ALL
            SELECT id FROM pending_events WHERE 
                title ~ '<[^>]*>' OR description ~ '<[^>]*>' OR location ~ '<[^>]*>' OR 
                location_url ~ '<[^>]*>' OR admin_notes ~ '<[^>]*>' OR
                title ~ '&(nbsp|amp|lt|gt|quot|#39);' OR description ~ '&(nbsp|amp|lt|gt|quot|#39);' OR 
                location ~ '&(nbsp|amp|lt|gt|quot|#39);' OR admin_notes ~ '&(nbsp|amp|lt|gt|quot|#39);'
        ) AS dirty_records
    "#)
    .fetch_one(pool)
    .await?;
    
    Ok(count.get::<i64, _>(0))
}

async fn clean_bulletins(pool: &PgPool) -> Result<u32, sqlx::Error> {
    println!("🔧 Cleaning bulletins table...");
    
    // Get all bulletins that need cleaning
    let rows = sqlx::query!(r#"
        SELECT id, title, sabbath_school, divine_worship, scripture_reading, sunset 
        FROM bulletins 
        WHERE 
            title ~ '<[^>]*>' OR sabbath_school ~ '<[^>]*>' OR divine_worship ~ '<[^>]*>' OR 
            scripture_reading ~ '<[^>]*>' OR sunset ~ '<[^>]*>' OR
            title ~ '&(nbsp|amp|lt|gt|quot|#39);' OR sabbath_school ~ '&(nbsp|amp|lt|gt|quot|#39);' OR 
            divine_worship ~ '&(nbsp|amp|lt|gt|quot|#39);' OR scripture_reading ~ '&(nbsp|amp|lt|gt|quot|#39);' OR 
            sunset ~ '&(nbsp|amp|lt|gt|quot|#39);'
    "#)
    .fetch_all(pool)
    .await?;

    let mut cleaned_count = 0;
    
    for row in rows {
        let clean_title = strip_html_tags(&row.title);
        let clean_sabbath_school = row.sabbath_school.as_ref().map(|s| strip_html_tags(s));
        let clean_divine_worship = row.divine_worship.as_ref().map(|s| strip_html_tags(s));
        let clean_scripture_reading = row.scripture_reading.as_ref().map(|s| strip_html_tags(s));
        let clean_sunset = row.sunset.as_ref().map(|s| strip_html_tags(s));

        sqlx::query!(r#"
            UPDATE bulletins 
            SET title = $2, sabbath_school = $3, divine_worship = $4, scripture_reading = $5, sunset = $6, updated_at = NOW()
            WHERE id = $1
        "#, 
            row.id, clean_title, clean_sabbath_school, clean_divine_worship, clean_scripture_reading, clean_sunset
        )
        .execute(pool)
        .await?;
        
        cleaned_count += 1;
    }
    
    println!("   ✅ Cleaned {} bulletin records", cleaned_count);
    Ok(cleaned_count as u32)
}

async fn clean_events(pool: &PgPool) -> Result<u32, sqlx::Error> {
    println!("🔧 Cleaning events table...");
    
    let rows = sqlx::query!(r#"
        SELECT id, title, description, location, location_url, approved_from
        FROM events 
        WHERE 
            title ~ '<[^>]*>' OR description ~ '<[^>]*>' OR location ~ '<[^>]*>' OR 
            location_url ~ '<[^>]*>' OR approved_from ~ '<[^>]*>' OR
            title ~ '&(nbsp|amp|lt|gt|quot|#39);' OR description ~ '&(nbsp|amp|lt|gt|quot|#39);' OR 
            location ~ '&(nbsp|amp|lt|gt|quot|#39);' OR location_url ~ '&(nbsp|amp|lt|gt|quot|#39);' OR 
            approved_from ~ '&(nbsp|amp|lt|gt|quot|#39);'
    "#)
    .fetch_all(pool)
    .await?;

    let mut cleaned_count = 0;
    
    for row in rows {
        let clean_title = strip_html_tags(&row.title);
        let clean_description = strip_html_tags(&row.description);
        let clean_location = strip_html_tags(&row.location);
        let clean_location_url = row.location_url.as_ref().map(|s| strip_html_tags(s));
        let clean_approved_from = row.approved_from.as_ref().map(|s| strip_html_tags(s));

        sqlx::query!(r#"
            UPDATE events 
            SET title = $2, description = $3, location = $4, location_url = $5, approved_from = $6, updated_at = NOW()
            WHERE id = $1
        "#, 
            row.id, clean_title, clean_description, clean_location, clean_location_url, clean_approved_from
        )
        .execute(pool)
        .await?;
        
        cleaned_count += 1;
    }
    
    println!("   ✅ Cleaned {} event records", cleaned_count);
    Ok(cleaned_count as u32)
}

async fn clean_pending_events(pool: &PgPool) -> Result<u32, sqlx::Error> {
    println!("🔧 Cleaning pending_events table...");
    
    let rows = sqlx::query!(r#"
        SELECT id, title, description, location, location_url, admin_notes, submitter_email, bulletin_week
        FROM pending_events 
        WHERE 
            title ~ '<[^>]*>' OR description ~ '<[^>]*>' OR location ~ '<[^>]*>' OR 
            location_url ~ '<[^>]*>' OR admin_notes ~ '<[^>]*>' OR submitter_email ~ '<[^>]*>' OR
            bulletin_week ~ '<[^>]*>' OR
            title ~ '&(nbsp|amp|lt|gt|quot|#39);' OR description ~ '&(nbsp|amp|lt|gt|quot|#39);' OR 
            location ~ '&(nbsp|amp|lt|gt|quot|#39);' OR location_url ~ '&(nbsp|amp|lt|gt|quot|#39);' OR 
            admin_notes ~ '&(nbsp|amp|lt|gt|quot|#39);' OR submitter_email ~ '&(nbsp|amp|lt|gt|quot|#39);' OR
            bulletin_week ~ '&(nbsp|amp|lt|gt|quot|#39);'
    "#)
    .fetch_all(pool)
    .await?;

    let mut cleaned_count = 0;
    
    for row in rows {
        let clean_title = strip_html_tags(&row.title);
        let clean_description = strip_html_tags(&row.description);
        let clean_location = strip_html_tags(&row.location);
        let clean_location_url = row.location_url.as_ref().map(|s| strip_html_tags(s));
        let clean_admin_notes = row.admin_notes.as_ref().map(|s| strip_html_tags(s));
        let clean_submitter_email = row.submitter_email.as_ref().map(|s| strip_html_tags(s));
        let clean_bulletin_week = strip_html_tags(&row.bulletin_week);

        sqlx::query!(r#"
            UPDATE pending_events 
            SET title = $2, description = $3, location = $4, location_url = $5, admin_notes = $6, 
                submitter_email = $7, bulletin_week = $8, updated_at = NOW()
            WHERE id = $1
        "#, 
            row.id, clean_title, clean_description, clean_location, clean_location_url, 
            clean_admin_notes, clean_submitter_email, clean_bulletin_week
        )
        .execute(pool)
        .await?;
        
        cleaned_count += 1;
    }
    
    println!("   ✅ Cleaned {} pending event records", cleaned_count);
    Ok(cleaned_count as u32)
}

async fn clean_members(pool: &PgPool) -> Result<u32, sqlx::Error> {
    println!("🔧 Cleaning members table...");
    
    let rows = sqlx::query!(r#"
        SELECT id, first_name, last_name, address, notes, emergency_contact_name, membership_status
        FROM members 
        WHERE 
            first_name ~ '<[^>]*>' OR last_name ~ '<[^>]*>' OR address ~ '<[^>]*>' OR 
            notes ~ '<[^>]*>' OR emergency_contact_name ~ '<[^>]*>' OR membership_status ~ '<[^>]*>' OR
            first_name ~ '&(nbsp|amp|lt|gt|quot|#39);' OR last_name ~ '&(nbsp|amp|lt|gt|quot|#39);' OR 
            address ~ '&(nbsp|amp|lt|gt|quot|#39);' OR notes ~ '&(nbsp|amp|lt|gt|quot|#39);' OR 
            emergency_contact_name ~ '&(nbsp|amp|lt|gt|quot|#39);' OR membership_status ~ '&(nbsp|amp|lt|gt|quot|#39);'
    "#)
    .fetch_all(pool)
    .await?;

    let mut cleaned_count = 0;
    
    for row in rows {
        let clean_first_name = strip_html_tags(&row.first_name);
        let clean_last_name = strip_html_tags(&row.last_name);
        let clean_address = row.address.as_ref().map(|s| strip_html_tags(s));
        let clean_notes = row.notes.as_ref().map(|s| strip_html_tags(s));
        let clean_emergency_contact_name = row.emergency_contact_name.as_ref().map(|s| strip_html_tags(s));
        let clean_membership_status = row.membership_status.as_ref().map(|s| strip_html_tags(s));

        sqlx::query!(r#"
            UPDATE members 
            SET first_name = $2, last_name = $3, address = $4, notes = $5, emergency_contact_name = $6, 
                membership_status = $7, updated_at = NOW()
            WHERE id = $1
        "#, 
            row.id, clean_first_name, clean_last_name, clean_address, clean_notes, 
            clean_emergency_contact_name, clean_membership_status
        )
        .execute(pool)
        .await?;
        
        cleaned_count += 1;
    }
    
    println!("   ✅ Cleaned {} member records", cleaned_count);
    Ok(cleaned_count as u32)
}

async fn clean_church_config(pool: &PgPool) -> Result<u32, sqlx::Error> {
    println!("🔧 Cleaning church_config table...");
    
    let rows = sqlx::query!(r#"
        SELECT id, church_name, contact_email, church_address, po_box, google_maps_url, about_text
        FROM church_config 
        WHERE 
            church_name ~ '<[^>]*>' OR contact_email ~ '<[^>]*>' OR church_address ~ '<[^>]*>' OR 
            po_box ~ '<[^>]*>' OR google_maps_url ~ '<[^>]*>' OR about_text ~ '<[^>]*>' OR
            church_name ~ '&(nbsp|amp|lt|gt|quot|#39);' OR contact_email ~ '&(nbsp|amp|lt|gt|quot|#39);' OR 
            church_address ~ '&(nbsp|amp|lt|gt|quot|#39);' OR po_box ~ '&(nbsp|amp|lt|gt|quot|#39);' OR 
            google_maps_url ~ '&(nbsp|amp|lt|gt|quot|#39);' OR about_text ~ '&(nbsp|amp|lt|gt|quot|#39);'
    "#)
    .fetch_all(pool)
    .await?;

    let mut cleaned_count = 0;
    
    for row in rows {
        let clean_church_name = strip_html_tags(&row.church_name);
        let clean_contact_email = strip_html_tags(&row.contact_email);
        let clean_church_address = strip_html_tags(&row.church_address);
        let clean_po_box = row.po_box.as_ref().map(|s| strip_html_tags(s));
        let clean_google_maps_url = row.google_maps_url.as_ref().map(|s| strip_html_tags(s));
        let clean_about_text = strip_html_tags(&row.about_text);

        sqlx::query!(r#"
            UPDATE church_config 
            SET church_name = $2, contact_email = $3, church_address = $4, po_box = $5, 
                google_maps_url = $6, about_text = $7, updated_at = NOW()
            WHERE id = $1
        "#, 
            row.id, clean_church_name, clean_contact_email, clean_church_address, 
            clean_po_box, clean_google_maps_url, clean_about_text
        )
        .execute(pool)
        .await?;
        
        cleaned_count += 1;
    }
    
    println!("   ✅ Cleaned {} church config records", cleaned_count);
    Ok(cleaned_count as u32)
}

async fn clean_users(pool: &PgPool) -> Result<u32, sqlx::Error> {
    println!("🔧 Cleaning users table...");
    
    let rows = sqlx::query!(r#"
        SELECT id, username, email, name, avatar_url, role
        FROM users 
        WHERE 
            username ~ '<[^>]*>' OR email ~ '<[^>]*>' OR name ~ '<[^>]*>' OR 
            avatar_url ~ '<[^>]*>' OR role ~ '<[^>]*>' OR
            username ~ '&(nbsp|amp|lt|gt|quot|#39);' OR email ~ '&(nbsp|amp|lt|gt|quot|#39);' OR 
            name ~ '&(nbsp|amp|lt|gt|quot|#39);' OR avatar_url ~ '&(nbsp|amp|lt|gt|quot|#39);' OR 
            role ~ '&(nbsp|amp|lt|gt|quot|#39);'
    "#)
    .fetch_all(pool)
    .await?;

    let mut cleaned_count = 0;
    
    for row in rows {
        let clean_username = strip_html_tags(&row.username);
        let clean_email = row.email.as_ref().map(|s| strip_html_tags(s));
        let clean_name = row.name.as_ref().map(|s| strip_html_tags(s));
        let clean_avatar_url = row.avatar_url.as_ref().map(|s| strip_html_tags(s));
        let clean_role = row.role.as_ref().map(|s| strip_html_tags(s));

        sqlx::query!(r#"
            UPDATE users 
            SET username = $2, email = $3, name = $4, avatar_url = $5, role = $6, updated_at = NOW()
            WHERE id = $1
        "#, 
            row.id, clean_username, clean_email, clean_name, clean_avatar_url, clean_role
        )
        .execute(pool)
        .await?;
        
        cleaned_count += 1;
    }
    
    println!("   ✅ Cleaned {} user records", cleaned_count);
    Ok(cleaned_count as u32)
}

async fn clean_media_items(pool: &PgPool) -> Result<u32, sqlx::Error> {
    println!("🔧 Cleaning media_items table...");
    
    let rows = sqlx::query!(r#"
        SELECT id, title, speaker, description, scripture_reading
        FROM media_items 
        WHERE 
            title ~ '<[^>]*>' OR speaker ~ '<[^>]*>' OR description ~ '<[^>]*>' OR 
            scripture_reading ~ '<[^>]*>' OR
            title ~ '&(nbsp|amp|lt|gt|quot|#39);' OR speaker ~ '&(nbsp|amp|lt|gt|quot|#39);' OR 
            description ~ '&(nbsp|amp|lt|gt|quot|#39);' OR scripture_reading ~ '&(nbsp|amp|lt|gt|quot|#39);'
    "#)
    .fetch_all(pool)
    .await?;

    let mut cleaned_count = 0;
    
    for row in rows {
        let clean_title = strip_html_tags(&row.title);
        let clean_speaker = row.speaker.as_ref().map(|s| strip_html_tags(s));
        let clean_description = row.description.as_ref().map(|s| strip_html_tags(s));
        let clean_scripture_reading = row.scripture_reading.as_ref().map(|s| strip_html_tags(s));

        sqlx::query!(r#"
            UPDATE media_items 
            SET title = $2, speaker = $3, description = $4, scripture_reading = $5, updated_at = NOW()
            WHERE id = $1
        "#, 
            row.id, clean_title, clean_speaker, clean_description, clean_scripture_reading
        )
        .execute(pool)
        .await?;
        
        cleaned_count += 1;
    }
    
    println!("   ✅ Cleaned {} media item records", cleaned_count);
    Ok(cleaned_count as u32)
}

async fn clean_transcoded_media(pool: &PgPool) -> Result<u32, sqlx::Error> {
    println!("🔧 Cleaning transcoded_media table...");
    
    let rows = sqlx::query!(r#"
        SELECT id, error_message, transcoding_method
        FROM transcoded_media 
        WHERE 
            error_message ~ '<[^>]*>' OR transcoding_method ~ '<[^>]*>' OR
            error_message ~ '&(nbsp|amp|lt|gt|quot|#39);' OR transcoding_method ~ '&(nbsp|amp|lt|gt|quot|#39);'
    "#)
    .fetch_all(pool)
    .await?;

    let mut cleaned_count = 0;
    
    for row in rows {
        let clean_error_message = row.error_message.as_ref().map(|s| strip_html_tags(s));
        let clean_transcoding_method = row.transcoding_method.as_ref().map(|s| strip_html_tags(s));

        sqlx::query!(r#"
            UPDATE transcoded_media 
            SET error_message = $2, transcoding_method = $3, updated_at = NOW()
            WHERE id = $1
        "#, 
            row.id, clean_error_message, clean_transcoding_method
        )
        .execute(pool)
        .await?;
        
        cleaned_count += 1;
    }
    
    println!("   ✅ Cleaned {} transcoded media records", cleaned_count);
    Ok(cleaned_count as u32)
}