// src/db/backup_user.ts
/*

Implement this at some point


Solution 1: Use a Custom Backup/Restore Script (Recommended)
The most reliable way to handle this is to create dedicated scripts for backing up and restoring a user's data. These scripts can use SQL to intelligently query the database, trace all the foreign key relationships, and generate a complete, ordered SQL file for restoration.

How it works:

Create a Backup Script (backup_user.js):

This Node.js script would connect to the database.
It would take a user's email as an argument.
It would then systematically query all tables related to that user, starting with the users table and following the foreign keys to shopping_lists, shopping_list_items, watched_items, etc.
It would then generate a single user_backup.sql file containing all the INSERT statements needed to recreate that user's data, carefully ordered to respect foreign key constraints (e.g., insert shopping_lists before shopping_list_items).
Create a Restore Script (restore_user.js):

This script would connect to the database.
It would read the user_backup.sql file and execute its contents against the database, restoring the user's data into the newly created schema.
Update the Workflow:

The manual-db-reset.yml workflow would be simplified to call these scripts. The "Backup" step would run node backup_user.js tsx@gmail.com, and the "Restore" step would run node restore_user.js.

*/
/**
 * @file This script creates a data-only backup for a single user.
 * It traces relationships across tables to export all relevant data as SQL INSERT statements.
 *
 * Usage (from project root, after setting environment variables):
 * export DB_USER=... DB_PASSWORD=... && npx tsx src/db/backup_user.ts --email user@example.com --output user_backup.sql
 */

import { Pool, PoolClient } from 'pg';
import fs from 'node:fs/promises';
import { logger } from '../services/logger.server';

const pool = new Pool({
  user: process.env.DB_USER,
  host: process.env.DB_HOST,
  database: process.env.DB_NAME_PROD, // IMPORTANT: This script targets the production DB
  password: process.env.DB_PASSWORD,
  port: parseInt(process.env.DB_PORT || '5432', 10),
});

/**
 * A map defining the tables to back up and the column that links them to the user.
 * The order is critical to respect foreign key constraints on restore.
 * Tables with direct user_id foreign keys come first.
 */
const USER_DATA_TABLES: Record<string, string> = {
    'users': 'user_id',
    'profiles': 'user_id',
    'pantry_locations': 'user_id',
    'shopping_lists': 'user_id',
    'recipes': 'user_id',
    'menu_plans': 'user_id',
    'recipe_collections': 'user_id',
    'user_item_aliases': 'user_id',
    'user_appliances': 'user_id',
    'user_dietary_restrictions': 'user_id',
    'favorite_stores': 'user_id',
    'favorite_recipes': 'user_id',
    'user_watched_items': 'user_id',
    'receipts': 'user_id',
    'shopping_trips': 'user_id',
};

type DBValue = string | number | boolean | null | Date | object;

/**
 * Generates a SQL INSERT statement for a given row of data.
 * @param table The name of the table.
 * @param row The data object for the row.
 * @returns A formatted SQL INSERT statement string.
 */
function generateInsertStatement(table: string, row: Record<string, DBValue>): string {
    const columns = Object.keys(row).map(col => `"${col}"`).join(', ');
    const values = Object.values(row).map(val => {
        if (val === null) return 'NULL';
        if (val instanceof Date) return `'${val.toISOString()}'`; // Handle Date objects
        if (typeof val === 'string') return `'${val.replace(/'/g, "''")}'`; // Escape single quotes
        if (typeof val === 'object') return `'${JSON.stringify(val).replace(/'/g, "''")}'`; // Handle JSONB
        return val;
    }).join(', ');

    return `INSERT INTO public.${table} (${columns}) VALUES (${values});\n`;
}

async function main() {
    const args = process.argv.slice(2);
    const emailIndex = args.indexOf('--email');
    const outputIndex = args.indexOf('--output');

    if (emailIndex === -1 || outputIndex === -1) {
        console.error('Usage: tsx src/db/backup_user.ts --email <user_email> --output <output_file.sql>');
        process.exit(1);
    }

    const userEmail = args[emailIndex + 1];
    const outputFile = args[outputIndex + 1];
    let client: PoolClient | null = null;

    try {
        client = await pool.connect();
        logger.info(`Connected to database. Backing up user: ${userEmail}`);

        // 1. Find the user ID
        const userRes = await client.query('SELECT user_id FROM public.users WHERE email = $1', [userEmail]);
        if (userRes.rows.length === 0) {
            logger.warn(`User with email ${userEmail} not found. No backup will be created.`);
            return;
        }
        const userId = userRes.rows[0].user_id;
        logger.info(`Found user ID: ${userId}`);

        let backupSql = '-- User Data Backup\n';
        backupSql += `-- Generated at: ${new Date().toISOString()}\n`;
        backupSql += `-- User Email: ${userEmail}\n\n`;

        // 2. Backup data from tables with a direct user_id link
        for (const [table, column] of Object.entries(USER_DATA_TABLES)) {
            const res = await client.query(`SELECT * FROM public.${table} WHERE ${column} = $1`, [userId]);
            if (res.rows.length > 0) {
                backupSql += `-- Data for table: ${table}\n`;
                for (const row of res.rows) {
                    backupSql += generateInsertStatement(table, row);
                }
                backupSql += '\n';
            }
        }

        // 3. Backup data from indirectly related tables (e.g., shopping_list_items)
        const shoppingListsRes = await client.query('SELECT shopping_list_id FROM public.shopping_lists WHERE user_id = $1', [userId]);
        const shoppingListIds = shoppingListsRes.rows.map(r => r.shopping_list_id);

        if (shoppingListIds.length > 0) {
            const itemsRes = await client.query('SELECT * FROM public.shopping_list_items WHERE shopping_list_id = ANY($1)', [shoppingListIds]);
            if (itemsRes.rows.length > 0) {
                backupSql += '-- Data for table: shopping_list_items\n';
                for (const row of itemsRes.rows) {
                    backupSql += generateInsertStatement('shopping_list_items', row);
                }
                backupSql += '\n';
            }
        }

        // (Add similar logic for other indirectly related tables like recipe_ingredients, planned_meals, etc.)

        // 4. Write the final SQL to the output file
        await fs.writeFile(outputFile, backupSql);
        logger.info(`✅ Successfully created backup for user ${userEmail} at ${outputFile}`);

    } catch (error) {
        logger.error('Failed to create user backup.', { error });
        process.exit(1);
    } finally {
        client?.release();
        await pool.end();
    }
}

main();
