-- sql/03_ddl.sql
-- DDL generation for register infrastructure
-- Creates tables, indexes when a new register is created

-- ============================================================
-- HELPER: Build dimension column SQL fragment
-- Returns comma-prefixed text: ", warehouse int, product int"
-- ============================================================
CREATE OR REPLACE FUNCTION @extschema@._build_dim_columns(p_dimensions jsonb)
RETURNS text
LANGUAGE plpgsql IMMUTABLE AS $$
DECLARE
    dim_key  text;
    dim_type text;
    result   text := '';
BEGIN
    FOR dim_key, dim_type IN SELECT * FROM jsonb_each_text(p_dimensions)
    LOOP
        result := result || format(', %I %s', dim_key, dim_type);
    END LOOP;
    RETURN result;
END;
$$;

-- ============================================================
-- HELPER: Build resource column SQL fragment
-- Returns comma-prefixed text: ", quantity numeric NOT NULL DEFAULT 0"
-- ============================================================
CREATE OR REPLACE FUNCTION @extschema@._build_res_columns(p_resources jsonb)
RETURNS text
LANGUAGE plpgsql IMMUTABLE AS $$
DECLARE
    res_key  text;
    res_type text;
    result   text := '';
BEGIN
    FOR res_key, res_type IN SELECT * FROM jsonb_each_text(p_resources)
    LOOP
        result := result || format(', %I %s NOT NULL DEFAULT 0', res_key, res_type);
    END LOOP;
    RETURN result;
END;
$$;

-- ============================================================
-- CREATE MOVEMENTS TABLE (partitioned by period)
-- ============================================================
CREATE OR REPLACE FUNCTION @extschema@._ddl_create_movements_table(
    p_name          text,
    p_recorder_type text,
    p_dimensions    jsonb,
    p_resources     jsonb
) RETURNS void
LANGUAGE plpgsql AS $$
DECLARE
    col_defs text;
    res_defs text;
BEGIN
    col_defs := @extschema@._build_dim_columns(p_dimensions);
    res_defs := @extschema@._build_res_columns(p_resources);

    EXECUTE format(
        'CREATE TABLE @extschema@.%I (
            id             uuid          DEFAULT gen_random_uuid(),
            recorded_at    timestamptz   DEFAULT now() NOT NULL,
            recorder       %s            NOT NULL,
            period         timestamptz   NOT NULL,
            movement_type  text          DEFAULT ''regular'' NOT NULL,
            dim_hash       bigint        NOT NULL
            %s
            %s,
            PRIMARY KEY (id, period)
        ) PARTITION BY RANGE (period)',
        p_name || '_movements',
        p_recorder_type,
        col_defs,
        res_defs
    );

    -- Default partition (catches periods without a specific partition)
    EXECUTE format(
        'CREATE TABLE @extschema@.%I PARTITION OF @extschema@.%I DEFAULT',
        p_name || '_movements_default',
        p_name || '_movements'
    );
END;
$$;

-- ============================================================
-- CREATE TOTALS TABLES (daily + monthly + yearly aggregations)
-- ============================================================
CREATE OR REPLACE FUNCTION @extschema@._ddl_create_totals_tables(
    p_name       text,
    p_dimensions jsonb,
    p_resources  jsonb
) RETURNS void
LANGUAGE plpgsql AS $$
DECLARE
    col_defs text;
    res_defs text;
BEGIN
    col_defs := @extschema@._build_dim_columns(p_dimensions);
    res_defs := @extschema@._build_res_columns(p_resources);

    -- totals_day: daily turnover aggregation
    EXECUTE format(
        'CREATE TABLE @extschema@.%I (
            dim_hash       bigint        NOT NULL,
            period         date          NOT NULL
            %s
            %s,
            PRIMARY KEY (dim_hash, period)
        )',
        p_name || '_totals_day',
        col_defs,
        res_defs
    );

    -- totals_month: monthly turnover aggregation
    EXECUTE format(
        'CREATE TABLE @extschema@.%I (
            dim_hash       bigint        NOT NULL,
            period         date          NOT NULL
            %s
            %s,
            PRIMARY KEY (dim_hash, period)
        )',
        p_name || '_totals_month',
        col_defs,
        res_defs
    );

    -- totals_year: yearly turnover aggregation
    EXECUTE format(
        'CREATE TABLE @extschema@.%I (
            dim_hash       bigint        NOT NULL,
            period         date          NOT NULL
            %s
            %s,
            PRIMARY KEY (dim_hash, period)
        )',
        p_name || '_totals_year',
        col_defs,
        res_defs
    );
END;
$$;

-- ============================================================
-- CREATE BALANCE CACHE (only for kind='balance')
-- ============================================================
CREATE OR REPLACE FUNCTION @extschema@._ddl_create_balance_cache(
    p_name       text,
    p_dimensions jsonb,
    p_resources  jsonb
) RETURNS void
LANGUAGE plpgsql AS $$
DECLARE
    col_defs text;
    res_defs text;
BEGIN
    col_defs := @extschema@._build_dim_columns(p_dimensions);
    res_defs := @extschema@._build_res_columns(p_resources);

    EXECUTE format(
        'CREATE TABLE @extschema@.%I (
            dim_hash         bigint          NOT NULL PRIMARY KEY
            %s
            %s,
            last_movement_at timestamptz     NOT NULL DEFAULT now(),
            last_movement_id uuid,
            version          bigint          NOT NULL DEFAULT 0
        )',
        p_name || '_balance_cache',
        col_defs,
        res_defs
    );
END;
$$;

-- ============================================================
-- CREATE DELTA BUFFER (only if high_write=true)
-- Unlogged table for fast append-only delta writes
-- ============================================================
CREATE OR REPLACE FUNCTION @extschema@._ddl_create_delta_buffer(
    p_name      text,
    p_resources jsonb
) RETURNS void
LANGUAGE plpgsql AS $$
DECLARE
    res_defs text;
BEGIN
    res_defs := @extschema@._build_res_columns(p_resources);

    EXECUTE format(
        'CREATE UNLOGGED TABLE @extschema@.%I (
            id         bigserial     PRIMARY KEY,
            dim_hash   bigint        NOT NULL
            %s,
            created_at timestamptz   DEFAULT now()
        )',
        p_name || '_balance_cache_delta',
        res_defs
    );
END;
$$;

-- ============================================================
-- CREATE ALL INDEXES for register infrastructure
-- ============================================================
CREATE OR REPLACE FUNCTION @extschema@._ddl_create_indexes(
    p_name       text,
    p_kind       text,
    p_dimensions jsonb,
    p_high_write boolean
) RETURNS void
LANGUAGE plpgsql AS $$
DECLARE
    dim_key text;
BEGIN
    -- Movements indexes
    EXECUTE format('CREATE INDEX ON @extschema@.%I (dim_hash, period)',
        p_name || '_movements');
    EXECUTE format('CREATE INDEX ON @extschema@.%I (recorder)',
        p_name || '_movements');
    EXECUTE format('CREATE INDEX ON @extschema@.%I (period)',
        p_name || '_movements');

    -- Balance cache indexes: per-dimension for filtered queries
    IF p_kind = 'balance' THEN
        FOR dim_key IN SELECT * FROM jsonb_object_keys(p_dimensions)
        LOOP
            EXECUTE format('CREATE INDEX ON @extschema@.%I (%I)',
                p_name || '_balance_cache', dim_key);
        END LOOP;
    END IF;

    -- Delta buffer indexes
    IF p_high_write THEN
        EXECUTE format('CREATE INDEX ON @extschema@.%I (dim_hash)',
            p_name || '_balance_cache_delta');
        EXECUTE format('CREATE INDEX ON @extschema@.%I (created_at)',
            p_name || '_balance_cache_delta');
    END IF;
END;
$$;

-- ============================================================
-- DROP ALL REGISTER INFRASTRUCTURE
-- Drops tables (CASCADE removes triggers), trigger functions,
-- and hash function for the given register name.
-- Uses IF EXISTS for idempotent cleanup.
-- ============================================================
CREATE OR REPLACE FUNCTION @extschema@._ddl_drop_infrastructure(p_name text)
RETURNS void
LANGUAGE plpgsql AS $$
BEGIN
    -- Drop tables (CASCADE removes attached triggers)
    EXECUTE format('DROP TABLE IF EXISTS @extschema@.%I CASCADE',
        p_name || '_movements');
    EXECUTE format('DROP TABLE IF EXISTS @extschema@.%I CASCADE',
        p_name || '_totals_day');
    EXECUTE format('DROP TABLE IF EXISTS @extschema@.%I CASCADE',
        p_name || '_totals_month');
    EXECUTE format('DROP TABLE IF EXISTS @extschema@.%I CASCADE',
        p_name || '_totals_year');
    EXECUTE format('DROP TABLE IF EXISTS @extschema@.%I CASCADE',
        p_name || '_balance_cache');
    EXECUTE format('DROP TABLE IF EXISTS @extschema@.%I CASCADE',
        p_name || '_balance_cache_delta');

        -- Drop trigger functions (not removed by DROP TABLE CASCADE)
    BEGIN
        EXECUTE format('DROP FUNCTION IF EXISTS @extschema@.%I CASCADE',
            '_trg_' || p_name || '_before_insert');
    EXCEPTION WHEN OTHERS THEN
        RAISE NOTICE 'Skipping drop of trigger function %: %', '_trg_' || p_name || '_before_insert', SQLERRM;
    END;
    BEGIN
        EXECUTE format('DROP FUNCTION IF EXISTS @extschema@.%I CASCADE',
            '_trg_' || p_name || '_after_insert');
    EXCEPTION WHEN OTHERS THEN
        RAISE NOTICE 'Skipping drop of trigger function %: %', '_trg_' || p_name || '_after_insert', SQLERRM;
    END;
    BEGIN
        EXECUTE format('DROP FUNCTION IF EXISTS @extschema@.%I CASCADE',
            '_trg_' || p_name || '_after_delete');
    EXCEPTION WHEN OTHERS THEN
        RAISE NOTICE 'Skipping drop of trigger function %: %', '_trg_' || p_name || '_after_delete', SQLERRM;
    END;
    BEGIN
        EXECUTE format('DROP FUNCTION IF EXISTS @extschema@.%I CASCADE',
            '_trg_' || p_name || '_block_update');
    EXCEPTION WHEN OTHERS THEN
        RAISE NOTICE 'Skipping drop of trigger function %: %', '_trg_' || p_name || '_block_update', SQLERRM;
    END;
    BEGIN
        EXECUTE format('DROP FUNCTION IF EXISTS @extschema@.%I CASCADE',
            '_trg_' || p_name || '_protect_derived');
    EXCEPTION WHEN OTHERS THEN
        RAISE NOTICE 'Skipping drop of trigger function %: %', '_trg_' || p_name || '_protect_derived', SQLERRM;
    END;

    -- Drop read functions (balance/turnover/movements)
    BEGIN
        PERFORM @extschema@._drop_read_functions(p_name);
    EXCEPTION WHEN OTHERS THEN
        RAISE NOTICE 'Skipping drop of read functions for %: %', p_name, SQLERRM;
    END;

    -- Drop hash function
    BEGIN
        PERFORM @extschema@._drop_hash_function(p_name);
    EXCEPTION WHEN OTHERS THEN
        RAISE NOTICE 'Skipping drop of hash functions for %: %', p_name, SQLERRM;
    END;
END;
$$;
