using Microsoft.EntityFrameworkCore.Migrations;

namespace Tiku.Infrastructure.Persistence.Migrations;

internal static class MigrationBuilderDatabaseObjectExtensions
{
    public static void EnsureAuthorizationCacheInvalidationTriggers(this MigrationBuilder migrationBuilder)
    {
        migrationBuilder.Sql("""
            INSERT INTO authorization_scope_versions
                (id, realm, tenant_id, version, created_at, updated_at)
            VALUES
                (gen_random_uuid(), 'platform', NULL, 1, now(), now())
            ON CONFLICT DO NOTHING;

            INSERT INTO authorization_scope_versions
                (id, realm, tenant_id, version, created_at, updated_at)
            SELECT gen_random_uuid(), 'tenant', id, 1, now(), now()
            FROM tenants
            ON CONFLICT DO NOTHING;

            CREATE OR REPLACE FUNCTION tiku_bump_tenant_authorization_version()
            RETURNS trigger
            LANGUAGE plpgsql
            AS $$
            DECLARE
                scope_tenant_id uuid;
                next_version bigint;
            BEGIN
                IF TG_OP = 'DELETE' THEN
                    scope_tenant_id := OLD.tenant_id;
                ELSE
                    scope_tenant_id := NEW.tenant_id;
                END IF;

                UPDATE authorization_scope_versions
                SET version = version + 1, updated_at = now()
                WHERE realm = 'tenant' AND tenant_id = scope_tenant_id
                RETURNING version INTO next_version;

                IF next_version IS NOT NULL THEN
                    INSERT INTO authorization_cache_invalidations
                        (id, target_type, tenant_id, realm, version, attempt_count, created_at, updated_at)
                    VALUES
                        (gen_random_uuid(), 'scope', scope_tenant_id, 'tenant', next_version, 0, now(), now());
                END IF;
                IF TG_OP = 'DELETE' THEN RETURN OLD; END IF;
                RETURN NEW;
            END;
            $$;

            CREATE OR REPLACE FUNCTION tiku_bump_platform_authorization_version()
            RETURNS trigger
            LANGUAGE plpgsql
            AS $$
            DECLARE next_version bigint;
            BEGIN
                UPDATE authorization_scope_versions
                SET version = version + 1, updated_at = now()
                WHERE realm = 'platform' AND tenant_id IS NULL
                RETURNING version INTO next_version;
                INSERT INTO authorization_cache_invalidations
                    (id, target_type, realm, version, attempt_count, created_at, updated_at)
                VALUES
                    (gen_random_uuid(), 'scope', 'platform', next_version, 0, now(), now());
                IF TG_OP = 'DELETE' THEN RETURN OLD; END IF;
                RETURN NEW;
            END;
            $$;

            CREATE OR REPLACE FUNCTION tiku_bump_all_authorization_versions()
            RETURNS trigger
            LANGUAGE plpgsql
            AS $$
            BEGIN
                UPDATE authorization_scope_versions SET version = version + 1, updated_at = now();
                INSERT INTO authorization_cache_invalidations
                    (id, target_type, tenant_id, realm, version, attempt_count, created_at, updated_at)
                SELECT gen_random_uuid(), 'scope', tenant_id, realm, version, 0, now(), now()
                FROM authorization_scope_versions;
                IF TG_OP = 'DELETE' THEN RETURN OLD; END IF;
                RETURN NEW;
            END;
            $$;

            DROP TRIGGER IF EXISTS trg_tenant_backend_roles_authorization_version ON tenant_backend_roles;
            DROP TRIGGER IF EXISTS trg_tenant_backend_role_permissions_authorization_version ON tenant_backend_role_permissions;
            DROP TRIGGER IF EXISTS trg_tenant_backend_user_roles_authorization_version ON tenant_backend_user_roles;
            DROP TRIGGER IF EXISTS trg_platform_backend_roles_authorization_version ON platform_backend_roles;
            DROP TRIGGER IF EXISTS trg_platform_backend_role_permissions_authorization_version ON platform_backend_role_permissions;
            DROP TRIGGER IF EXISTS trg_platform_backend_user_roles_authorization_version ON platform_backend_user_roles;
            DROP TRIGGER IF EXISTS trg_backend_permissions_authorization_version ON backend_permissions;

            CREATE TRIGGER trg_tenant_backend_roles_authorization_version
            AFTER INSERT OR UPDATE OR DELETE ON tenant_backend_roles
            FOR EACH ROW EXECUTE FUNCTION tiku_bump_tenant_authorization_version();
            CREATE TRIGGER trg_tenant_backend_role_permissions_authorization_version
            AFTER INSERT OR UPDATE OR DELETE ON tenant_backend_role_permissions
            FOR EACH ROW EXECUTE FUNCTION tiku_bump_tenant_authorization_version();
            CREATE TRIGGER trg_tenant_backend_user_roles_authorization_version
            AFTER INSERT OR UPDATE OR DELETE ON tenant_backend_user_roles
            FOR EACH ROW EXECUTE FUNCTION tiku_bump_tenant_authorization_version();

            CREATE TRIGGER trg_platform_backend_roles_authorization_version
            AFTER INSERT OR UPDATE OR DELETE ON platform_backend_roles
            FOR EACH ROW EXECUTE FUNCTION tiku_bump_platform_authorization_version();
            CREATE TRIGGER trg_platform_backend_role_permissions_authorization_version
            AFTER INSERT OR UPDATE OR DELETE ON platform_backend_role_permissions
            FOR EACH ROW EXECUTE FUNCTION tiku_bump_platform_authorization_version();
            CREATE TRIGGER trg_platform_backend_user_roles_authorization_version
            AFTER INSERT OR UPDATE OR DELETE ON platform_backend_user_roles
            FOR EACH ROW EXECUTE FUNCTION tiku_bump_platform_authorization_version();

            CREATE TRIGGER trg_backend_permissions_authorization_version
            AFTER INSERT OR UPDATE OR DELETE ON backend_permissions
            FOR EACH STATEMENT EXECUTE FUNCTION tiku_bump_all_authorization_versions();
            """);
    }

    public static void DropAuthorizationCacheInvalidationTriggers(this MigrationBuilder migrationBuilder)
    {
        migrationBuilder.Sql("""
            DROP FUNCTION IF EXISTS tiku_bump_tenant_authorization_version() CASCADE;
            DROP FUNCTION IF EXISTS tiku_bump_platform_authorization_version() CASCADE;
            DROP FUNCTION IF EXISTS tiku_bump_all_authorization_versions() CASCADE;
            """);
    }

    public static void EnsureQueryOptimizationIndexes(this MigrationBuilder migrationBuilder)
    {
        migrationBuilder.Sql("""
            CREATE INDEX IF NOT EXISTS ix_scoreline_records_tenant_id_year_school_name_major_name_id
            ON scoreline_records (tenant_id, year DESC, school_name, major_name, id);
            CREATE INDEX IF NOT EXISTS ix_scoreline_records_field_values_jsonb_path
            ON scoreline_records USING gin (field_values jsonb_path_ops);
            CREATE INDEX IF NOT EXISTS ix_scoreline_records_school_name_trgm
            ON scoreline_records USING gin (school_name gin_trgm_ops) WHERE school_name IS NOT NULL;
            CREATE INDEX IF NOT EXISTS ix_scoreline_records_major_name_trgm
            ON scoreline_records USING gin (major_name gin_trgm_ops) WHERE major_name IS NOT NULL;
            CREATE INDEX IF NOT EXISTS ix_background_jobs_pending_due
            ON background_jobs (run_after, created_at, id) WHERE status = 'pending';
            CREATE INDEX IF NOT EXISTS ix_background_jobs_processing_lease
            ON background_jobs (lock_expires_at, created_at, id) WHERE status = 'processing';
            """);
    }

    public static void DropQueryOptimizationIndexes(this MigrationBuilder migrationBuilder)
    {
        migrationBuilder.Sql("""
            DROP INDEX IF EXISTS ix_background_jobs_processing_lease;
            DROP INDEX IF EXISTS ix_background_jobs_pending_due;
            DROP INDEX IF EXISTS ix_scoreline_records_major_name_trgm;
            DROP INDEX IF EXISTS ix_scoreline_records_school_name_trgm;
            DROP INDEX IF EXISTS ix_scoreline_records_field_values_jsonb_path;
            DROP INDEX IF EXISTS ix_scoreline_records_tenant_id_year_school_name_major_name_id;
            """);
    }
}
