"""
Add strategic database indexes for hybrid filtering in rides.

This migration creates optimized indexes for the hybrid filtering system,
enabling sub-second query performance across all filter combinations.

Index Strategy:
- Composite indexes for common filter combinations
- Partial indexes for status-based filtering
- Covering indexes to avoid table lookups
- GIN indexes for full-text search
- Individual indexes for range queries

Performance Target: <100ms for most filter combinations
"""

from django.db import migrations


class Migration(migrations.Migration):
    dependencies = [
        ("rides", "0019_populate_hybrid_filtering_fields"),
    ]

    operations = [
        # Composite index for park + category filtering (very common)
        migrations.RunSQL(
            "CREATE INDEX rides_ride_park_category_idx ON rides_ride (park_id, category) WHERE category != '';",
            reverse_sql="DROP INDEX IF EXISTS rides_ride_park_category_idx;",
        ),
        # Composite index for park + status filtering (common)
        migrations.RunSQL(
            "CREATE INDEX rides_ride_park_status_idx ON rides_ride (park_id, status);",
            reverse_sql="DROP INDEX IF EXISTS rides_ride_park_status_idx;",
        ),
        # Composite index for category + status filtering
        migrations.RunSQL(
            "CREATE INDEX rides_ride_category_status_idx ON rides_ride (category, status) WHERE category != '';",
            reverse_sql="DROP INDEX IF EXISTS rides_ride_category_status_idx;",
        ),
        # Composite index for manufacturer + category
        migrations.RunSQL(
            "CREATE INDEX rides_ride_manufacturer_category_idx ON rides_ride (manufacturer_id, category) WHERE manufacturer_id IS NOT NULL AND category != '';",
            reverse_sql="DROP INDEX IF EXISTS rides_ride_manufacturer_category_idx;",
        ),
        # Composite index for opening year + category (for timeline filtering)
        migrations.RunSQL(
            "CREATE INDEX rides_ride_opening_year_category_idx ON rides_ride (opening_year, category) WHERE opening_year IS NOT NULL AND category != '';",
            reverse_sql="DROP INDEX IF EXISTS rides_ride_opening_year_category_idx;",
        ),
        # Partial index for operating rides only (most common filter)
        migrations.RunSQL(
            "CREATE INDEX rides_ride_operating_only_idx ON rides_ride (park_id, category, opening_year) WHERE status = 'OPERATING';",
            reverse_sql="DROP INDEX IF EXISTS rides_ride_operating_only_idx;",
        ),
        # Partial index for roller coasters only (popular category)
        migrations.RunSQL(
            "CREATE INDEX rides_ride_roller_coasters_idx ON rides_ride (park_id, status, opening_year) WHERE category = 'RC';",
            reverse_sql="DROP INDEX IF EXISTS rides_ride_roller_coasters_idx;",
        ),
        # Covering index for list views (includes commonly displayed fields)
        migrations.RunSQL(
            "CREATE INDEX rides_ride_list_covering_idx ON rides_ride (park_id, category, status) INCLUDE (name, opening_date, average_rating);",
            reverse_sql="DROP INDEX IF EXISTS rides_ride_list_covering_idx;",
        ),
        # GIN index for full-text search on computed search_text field
        migrations.RunSQL(
            "CREATE INDEX rides_ride_search_text_gin_idx ON rides_ride USING gin(to_tsvector('english', search_text));",
            reverse_sql="DROP INDEX IF EXISTS rides_ride_search_text_gin_idx;",
        ),
        # Trigram index for fuzzy text search
        migrations.RunSQL(
            "CREATE INDEX rides_ride_search_text_trgm_idx ON rides_ride USING gin(search_text gin_trgm_ops);",
            reverse_sql="DROP INDEX IF EXISTS rides_ride_search_text_trgm_idx;",
        ),
        # Index for rating-based filtering
        migrations.RunSQL(
            "CREATE INDEX rides_ride_rating_idx ON rides_ride (average_rating) WHERE average_rating IS NOT NULL;",
            reverse_sql="DROP INDEX IF EXISTS rides_ride_rating_idx;",
        ),
        # Index for capacity-based filtering
        migrations.RunSQL(
            "CREATE INDEX rides_ride_capacity_idx ON rides_ride (capacity_per_hour) WHERE capacity_per_hour IS NOT NULL;",
            reverse_sql="DROP INDEX IF EXISTS rides_ride_capacity_idx;",
        ),
        # Index for height requirement filtering
        migrations.RunSQL(
            "CREATE INDEX rides_ride_height_req_idx ON rides_ride (min_height_in, max_height_in) WHERE min_height_in IS NOT NULL OR max_height_in IS NOT NULL;",
            reverse_sql="DROP INDEX IF EXISTS rides_ride_height_req_idx;",
        ),
        # Composite index for ride model filtering
        migrations.RunSQL(
            "CREATE INDEX rides_ride_model_manufacturer_idx ON rides_ride (ride_model_id, manufacturer_id) WHERE ride_model_id IS NOT NULL;",
            reverse_sql="DROP INDEX IF EXISTS rides_ride_model_manufacturer_idx;",
        ),
        # Index for designer filtering
        migrations.RunSQL(
            "CREATE INDEX rides_ride_designer_idx ON rides_ride (designer_id, category) WHERE designer_id IS NOT NULL;",
            reverse_sql="DROP INDEX IF EXISTS rides_ride_designer_idx;",
        ),
        # Index for park area filtering
        migrations.RunSQL(
            "CREATE INDEX rides_ride_park_area_idx ON rides_ride (park_area_id, status) WHERE park_area_id IS NOT NULL;",
            reverse_sql="DROP INDEX IF EXISTS rides_ride_park_area_idx;",
        ),
        # Roller coaster stats indexes for performance
        migrations.RunSQL(
            "CREATE INDEX rides_rollercoasterstats_height_idx ON rides_rollercoasterstats (height_ft) WHERE height_ft IS NOT NULL;",
            reverse_sql="DROP INDEX IF EXISTS rides_rollercoasterstats_height_idx;",
        ),
        migrations.RunSQL(
            "CREATE INDEX rides_rollercoasterstats_speed_idx ON rides_rollercoasterstats (speed_mph) WHERE speed_mph IS NOT NULL;",
            reverse_sql="DROP INDEX IF EXISTS rides_rollercoasterstats_speed_idx;",
        ),
        migrations.RunSQL(
            "CREATE INDEX rides_rollercoasterstats_inversions_idx ON rides_rollercoasterstats (inversions);",
            reverse_sql="DROP INDEX IF EXISTS rides_rollercoasterstats_inversions_idx;",
        ),
        migrations.RunSQL(
            "CREATE INDEX rides_rollercoasterstats_type_material_idx ON rides_rollercoasterstats (roller_coaster_type, track_material);",
            reverse_sql="DROP INDEX IF EXISTS rides_rollercoasterstats_type_material_idx;",
        ),
        migrations.RunSQL(
            "CREATE INDEX rides_rollercoasterstats_launch_type_idx ON rides_rollercoasterstats (launch_type);",
            reverse_sql="DROP INDEX IF EXISTS rides_rollercoasterstats_launch_type_idx;",
        ),
        # Composite index for complex roller coaster filtering
        migrations.RunSQL(
            "CREATE INDEX rides_rollercoasterstats_complex_idx ON rides_rollercoasterstats (roller_coaster_type, track_material, launch_type) INCLUDE (height_ft, speed_mph, inversions);",
            reverse_sql="DROP INDEX IF EXISTS rides_rollercoasterstats_complex_idx;",
        ),
        # Index for ride model filtering and search
        migrations.RunSQL(
            "CREATE INDEX rides_ridemodel_manufacturer_category_idx ON rides_ridemodel (manufacturer_id, category) WHERE manufacturer_id IS NOT NULL;",
            reverse_sql="DROP INDEX IF EXISTS rides_ridemodel_manufacturer_category_idx;",
        ),
        migrations.RunSQL(
            "CREATE INDEX rides_ridemodel_name_trgm_idx ON rides_ridemodel USING gin(name gin_trgm_ops);",
            reverse_sql="DROP INDEX IF EXISTS rides_ridemodel_name_trgm_idx;",
        ),
        # Index for company role-based filtering
        migrations.RunSQL(
            "CREATE INDEX rides_company_manufacturer_role_idx ON rides_company USING gin(roles) WHERE 'MANUFACTURER' = ANY(roles);",
            reverse_sql="DROP INDEX IF EXISTS rides_company_manufacturer_role_idx;",
        ),
        migrations.RunSQL(
            "CREATE INDEX rides_company_designer_role_idx ON rides_company USING gin(roles) WHERE 'DESIGNER' = ANY(roles);",
            reverse_sql="DROP INDEX IF EXISTS rides_company_designer_role_idx;",
        ),
        # Ensure trigram extension is available for fuzzy search
        migrations.RunSQL(
            "CREATE EXTENSION IF NOT EXISTS pg_trgm;", reverse_sql="-- Cannot safely drop pg_trgm extension"
        ),
    ]
