const { Pool } = require('pg'); const { DATABASE_URL } = require('./config'); const pool = new Pool({ connectionString: DATABASE_URL }); const initDB = async () => { try { await pool.query(`SET TIME ZONE 'America/Sao_Paulo';`); await pool.query(` CREATE TABLE IF NOT EXISTS orders ( id SERIAL PRIMARY KEY, cliente_nome VARCHAR(255), data_pedido VARCHAR(50), data_pedido_date DATE, valor_pedido NUMERIC(10, 2), produto_id VARCHAR(100), produto_descricao TEXT, quantidade INTEGER, valor_unitario NUMERIC(10, 5), created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP ); `); await pool.query(`ALTER TABLE orders ADD COLUMN IF NOT EXISTS pedido_id VARCHAR(100);`).catch(() => {}); await pool.query(`ALTER TABLE orders ADD COLUMN IF NOT EXISTS cliente_fone VARCHAR(50);`).catch(() => {}); await pool.query(`ALTER TABLE orders ADD COLUMN IF NOT EXISTS data_pedido_date DATE;`).catch(() => {}); await pool.query(`ALTER TABLE orders ADD COLUMN IF NOT EXISTS cliente_nome_fantasia VARCHAR(255);`).catch(() => {}); await pool.query(`ALTER TABLE orders ADD COLUMN IF NOT EXISTS id_vendedor VARCHAR(100);`).catch(() => {}); await pool.query(`ALTER TABLE orders ADD COLUMN IF NOT EXISTS nome_vendedor VARCHAR(255);`).catch(() => {}); await pool.query(`ALTER TABLE orders ADD COLUMN IF NOT EXISTS marketplace VARCHAR(255);`).catch(() => {}); await pool.query(`ALTER TABLE orders ADD COLUMN IF NOT EXISTS canal_venda VARCHAR(255);`).catch(() => {}); await pool.query(`ALTER TABLE orders ADD COLUMN IF NOT EXISTS numero_ecommerce VARCHAR(100);`).catch(() => {}); await pool.query(` ALTER TABLE orders ALTER COLUMN created_at TYPE TIMESTAMPTZ USING created_at AT TIME ZONE 'America/Sao_Paulo', ALTER COLUMN created_at SET DEFAULT CURRENT_TIMESTAMP; `).catch(() => {}); await pool.query(` UPDATE orders SET data_pedido_date = CASE WHEN data_pedido ~ '^\\d{4}[-/]\\d{1,2}[-/]\\d{1,2}' THEN to_date(replace(left(data_pedido, 10), '/', '-'), 'YYYY-MM-DD') WHEN data_pedido ~ '^\\d{1,2}[-/]\\d{1,2}[-/]\\d{4}' THEN to_date(replace(left(data_pedido, 10), '/', '-'), 'DD-MM-YYYY') ELSE NULL END WHERE data_pedido_date IS NULL AND data_pedido IS NOT NULL AND data_pedido != ''; `).catch(err => { console.error('Notice: Could not backfill normalized order dates:', err.message); }); await pool.query(`CREATE UNIQUE INDEX IF NOT EXISTS unique_order_product ON orders (pedido_id, produto_id);`).catch(err => { console.error('Notice: Could not create unique index (might already exist or there are duplicates):', err.message); }); await pool.query(` CREATE TABLE IF NOT EXISTS stock ( produto_id VARCHAR(100) PRIMARY KEY, nome TEXT, saldo INTEGER DEFAULT 0, delta_estoque INTEGER DEFAULT 0, updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP ); `); await pool.query(` ALTER TABLE stock ALTER COLUMN updated_at TYPE TIMESTAMPTZ USING updated_at AT TIME ZONE 'America/Sao_Paulo', ALTER COLUMN updated_at SET DEFAULT CURRENT_TIMESTAMP; `).catch(() => {}); await pool.query(` CREATE TABLE IF NOT EXISTS stock_campaign_queue ( id SERIAL PRIMARY KEY, base_product_name TEXT NOT NULL, produto_id VARCHAR(100) NOT NULL, nome TEXT NOT NULL, saldo INTEGER DEFAULT 0, delta_estoque INTEGER DEFAULT 0, status VARCHAR(20) DEFAULT 'pending', attempts INTEGER DEFAULT 0, last_error TEXT, created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, sent_at TIMESTAMPTZ ); `); await pool.query(` CREATE TABLE IF NOT EXISTS app_users ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL UNIQUE, password_hash TEXT NOT NULL, is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP ); `); await pool.query(` CREATE TABLE IF NOT EXISTS client_identity_tokens ( customer_key TEXT PRIMARY KEY, token TEXT NOT NULL UNIQUE, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); `); await pool.query(` ALTER TABLE app_users ALTER COLUMN created_at TYPE TIMESTAMPTZ USING created_at AT TIME ZONE 'America/Sao_Paulo', ALTER COLUMN created_at SET DEFAULT CURRENT_TIMESTAMP, ALTER COLUMN updated_at TYPE TIMESTAMPTZ USING updated_at AT TIME ZONE 'America/Sao_Paulo', ALTER COLUMN updated_at SET DEFAULT CURRENT_TIMESTAMP; `).catch(() => {}); await pool.query(` ALTER TABLE stock_campaign_queue ALTER COLUMN created_at TYPE TIMESTAMPTZ USING created_at AT TIME ZONE 'America/Sao_Paulo', ALTER COLUMN created_at SET DEFAULT CURRENT_TIMESTAMP, ALTER COLUMN updated_at TYPE TIMESTAMPTZ USING updated_at AT TIME ZONE 'America/Sao_Paulo', ALTER COLUMN updated_at SET DEFAULT CURRENT_TIMESTAMP, ALTER COLUMN sent_at TYPE TIMESTAMPTZ USING sent_at AT TIME ZONE 'America/Sao_Paulo'; `).catch(() => {}); await pool.query(` UPDATE stock_campaign_queue SET base_product_name = TRIM(regexp_replace( base_product_name, '\\s+-\\s+(?:(?:PP|P|M|G|GG|XG|XGG|EG|EGG|EXG|U|UNICO|ÚNICO|\\d{2})(?:/(?:PP|P|M|G|GG|XG|XGG|EG|EGG|EXG|U|UNICO|ÚNICO|\\d{2}))*)$', '', 'i' )) WHERE status IN ('pending', 'failed', 'processing') AND base_product_name ~* '\\s+-\\s+(?:(?:PP|P|M|G|GG|XG|XGG|EG|EGG|EXG|U|UNICO|ÚNICO|\\d{2})(?:/(?:PP|P|M|G|GG|XG|XGG|EG|EGG|EXG|U|UNICO|ÚNICO|\\d{2}))*)$'; `).catch(err => { console.error('Notice: Could not normalize queued campaign product names:', err.message); }); await pool.query(` UPDATE stock_campaign_queue SET base_product_name = nome WHERE status IN ('pending', 'failed', 'processing') AND nome ILIKE 'ETIQUETA%' AND base_product_name != nome; `).catch(err => { console.error('Notice: Could not restore queued etiqueta product names:', err.message); }); await pool.query(`CREATE INDEX IF NOT EXISTS idx_stock_campaign_queue_status ON stock_campaign_queue (status);`); await pool.query(`CREATE INDEX IF NOT EXISTS idx_orders_cliente_fone ON orders (cliente_fone);`); await pool.query(` CREATE INDEX IF NOT EXISTS idx_orders_normalized_cliente_nome ON orders ((NULLIF(LOWER(TRIM(regexp_replace(COALESCE(cliente_nome, ''), '\\s+', ' ', 'g'))), ''))); `).catch(err => { console.error('Notice: Could not create normalized client name index:', err.message); }); await pool.query(` CREATE INDEX IF NOT EXISTS idx_orders_normalized_cliente_nome_phone_date ON orders ( (NULLIF(LOWER(TRIM(regexp_replace(COALESCE(cliente_nome, ''), '\\s+', ' ', 'g'))), '')), data_pedido_date DESC, id DESC ) WHERE NULLIF(cliente_fone, '') IS NOT NULL; `).catch(err => { console.error('Notice: Could not create normalized client phone lookup index:', err.message); }); await pool.query(` CREATE INDEX IF NOT EXISTS idx_orders_customer_key_date ON orders ( (COALESCE(NULLIF(cliente_fone, ''), 'name:' || COALESCE(NULLIF(cliente_nome, ''), 'Cliente Desconhecido'))), data_pedido_date ); `); await pool.query(`CREATE INDEX IF NOT EXISTS idx_orders_produto_id ON orders (produto_id);`); await pool.query(`CREATE INDEX IF NOT EXISTS idx_orders_data_pedido_date ON orders (data_pedido_date);`); await pool.query(`CREATE INDEX IF NOT EXISTS idx_app_users_email ON app_users (LOWER(email));`); console.log('Database initialized successfully.'); } catch (err) { console.error('Failed to initialize database:', err); throw err; } }; module.exports = { pool, initDB };