//! Full-text + fuzzy search over the product catalog.
//!
//! Storefront search has to cope with Slovak text (diacritics, ad-hoc spelling)
//! and customer typos, while staying entirely inside Postgres — the catalog is
//! small (hundreds of products), so a separate search engine would be pure
//! operational overhead. This migration sets up:
//!
//!   1. `unaccent` + `pg_trgm` extensions, and an IMMUTABLE `f_unaccent` wrapper
//!      (the stock `unaccent` is only STABLE, so it can't be used in an index
//!      expression without wrapping it).
//!   2. a `sk_unaccent` text-search configuration: the `simple` dictionary
//!      (no English stemming, which would mangle Slovak) folded through
//!      `unaccent` so "kompresor" and "kompresór" tokenize identically.
//!   3. a STORED generated `products.search_vector`, weighting the name above
//!      the description, with a GIN index for `@@` matching.
//!   4. a trigram GIN index on the (unaccented) name for fuzzy matching.
//!
//! The matching query itself lives in `products::Entity::search`.

use sea_orm_migration::prelude::*;

#[derive(DeriveMigrationName)]
pub struct Migration;

#[async_trait::async_trait]
impl MigrationTrait for Migration {
    async fn up(&self, m: &SchemaManager) -> Result<(), DbErr> {
        let db = m.get_connection();

        db.execute_unprepared(
            r#"
            CREATE EXTENSION IF NOT EXISTS unaccent;
            CREATE EXTENSION IF NOT EXISTS pg_trgm;

            -- IMMUTABLE wrapper so unaccent() can be used in generated columns
            -- and index expressions (the extension's own unaccent() is STABLE).
            CREATE OR REPLACE FUNCTION f_unaccent(text)
            RETURNS text
            LANGUAGE sql IMMUTABLE PARALLEL SAFE STRICT AS
            $func$ SELECT public.unaccent('public.unaccent', $1) $func$;

            -- 'simple' (no stemming) + unaccent: a good fit for Slovak, where
            -- English stemming is wrong and accents are typed inconsistently.
            DROP TEXT SEARCH CONFIGURATION IF EXISTS sk_unaccent;
            CREATE TEXT SEARCH CONFIGURATION sk_unaccent ( COPY = simple );
            ALTER TEXT SEARCH CONFIGURATION sk_unaccent
                ALTER MAPPING FOR hword, hword_part, word
                WITH unaccent, simple;
            "#,
        )
        .await?;

        db.execute_unprepared(
            r#"
            ALTER TABLE products
                ADD COLUMN search_vector tsvector
                GENERATED ALWAYS AS (
                    setweight(to_tsvector('sk_unaccent', COALESCE(name, '')), 'A') ||
                    setweight(to_tsvector('sk_unaccent', COALESCE(description, '')), 'B')
                ) STORED;

            CREATE INDEX idx_products_search_vector
                ON products USING GIN (search_vector);
            CREATE INDEX idx_products_name_trgm
                ON products USING GIN (f_unaccent(name) gin_trgm_ops);
            "#,
        )
        .await?;

        Ok(())
    }

    async fn down(&self, m: &SchemaManager) -> Result<(), DbErr> {
        let db = m.get_connection();

        // Drop the trigram index (it depends on f_unaccent) before the function;
        // dropping the column takes its own GIN index with it.
        db.execute_unprepared(
            r#"
            DROP INDEX IF EXISTS idx_products_name_trgm;
            ALTER TABLE products DROP COLUMN IF EXISTS search_vector;
            DROP TEXT SEARCH CONFIGURATION IF EXISTS sk_unaccent;
            DROP FUNCTION IF EXISTS f_unaccent(text);
            "#,
        )
        .await?;

        // The unaccent / pg_trgm extensions are left installed: other objects may
        // rely on them and they are harmless on their own.
        Ok(())
    }
}
