/**
 * CLI Script: Import Atlas
 * Imports one atlas JSON source into PostgreSQL, replacing only that atlas schema
 * tree when safe and preserving the rest of the database.
 */
require('dotenv').config();
const path = require('path');
const fs = require('fs');
const { Pool } = require('pg');

const { createAtlasImageResolver } = require('../utils/atlasImageResolver');
const {
    getAtlasTranslationFilePath,
    partitionOptionImages,
    readAtlasJson,
    toImportedTraitId,
} = require('../utils/atlasImportUtils');

const ATLASES_DIR = path.join(__dirname, '..', 'data', 'atlases');
const IMAGES_DIR = path.join(__dirname, '..', 'public', 'images');
const resolveAtlasImage = createAtlasImageResolver(IMAGES_DIR);
const DEFAULT_ATLAS_ID = 'som-facial-somatoscopy-atlas';

function resolveAtlasFilePath(input) {
    if (!input) {
        throw new Error('Atlas file path or atlas file name is required. Example: node scripts/importAtlas.js somatoscopy.json');
    }

    const candidatePaths = [];

    if (path.isAbsolute(input)) {
        candidatePaths.push(input);
    } else {
        candidatePaths.push(path.join(process.cwd(), input));
        candidatePaths.push(path.join(ATLASES_DIR, input));
    }

    const existingPath = candidatePaths.find(candidatePath => fs.existsSync(candidatePath));
    if (!existingPath) {
        throw new Error(`Atlas file not found: ${input}`);
    }

    return existingPath;
}

async function upsertAtlasMetadata(client, atlasData, shouldBeDefault) {
    await client.query(
        `INSERT INTO atlases (id, nazev_cs, nazev_en, popis_cs, popis_en, je_vychozi)
         VALUES ($1, $2, $3, $4, $5, $6)
         ON CONFLICT (id) DO UPDATE SET
             nazev_cs = EXCLUDED.nazev_cs,
             nazev_en = EXCLUDED.nazev_en,
             popis_cs = EXCLUDED.popis_cs,
             popis_en = EXCLUDED.popis_en,
             je_vychozi = EXCLUDED.je_vychozi`,
        [
            atlasData.id,
            atlasData.label_cs,
            atlasData.label_en,
            atlasData.popis_cs,
            atlasData.popis_en,
            shouldBeDefault,
        ]
    );
}

async function replaceAtlasSchemaTree(client, atlasData) {
    await client.query('DELETE FROM trait_categories WHERE atlas_id = $1', [atlasData.id]);

    let categoryCount = 0;
    let traitCount = 0;
    let optionCount = 0;

    for (let categoryIndex = 0; categoryIndex < (atlasData.sections || []).length; categoryIndex += 1) {
        const section = atlasData.sections[categoryIndex];
        const categoryResult = await client.query(
            'INSERT INTO trait_categories (atlas_id, nazev_cs, nazev_en, poradi) VALUES ($1, $2, $3, $4) RETURNING id',
            [atlasData.id, section.nazev_cs, section.nazev_en, categoryIndex]
        );

        categoryCount += 1;
        const categoryId = categoryResult.rows[0].id;

        for (const trait of section.znaky || []) {
            const importedTraitId = toImportedTraitId(atlasData.id, trait.id);

            await client.query(
                'INSERT INTO traits (id, category_id, label_cs, label_en, popis_cs, popis_en) VALUES ($1, $2, $3, $4, $5, $6)',
                [importedTraitId, categoryId, trait.label_cs, trait.label_en, trait.popis_cs, trait.popis_en]
            );

            traitCount += 1;

            for (const option of trait.options || []) {
                const { photos, drawings } = partitionOptionImages(option.images, resolveAtlasImage);

                await client.query(
                    'INSERT INTO trait_options (trait_id, label_cs, label_en, images, drawings) VALUES ($1, $2, $3, $4, $5)',
                    [importedTraitId, option.label_cs, option.label_en, photos, drawings]
                );

                optionCount += 1;
            }
        }
    }

    return { categoryCount, traitCount, optionCount };
}

async function countAtlasAnnotations(client, atlasId) {
    const result = await client.query('SELECT COUNT(*)::int AS count FROM annotations WHERE atlas_id = $1', [atlasId]);
    return result.rows[0]?.count || 0;
}

async function importAtlas(atlasFileInput) {
    const atlasFilePath = resolveAtlasFilePath(atlasFileInput);
    const atlasData = readAtlasJson(atlasFilePath);

    if (!atlasData.id) {
        throw new Error(`Atlas file ${path.basename(atlasFilePath)} is missing required field: id`);
    }

    const pool = new Pool({
        user: process.env.DB_USER || 'postgres',
        host: process.env.DB_HOST || 'localhost',
        database: process.env.DB_NAME || (process.env.NODE_ENV === 'test' ? 'postgres_test' : 'postgres'),
        password: process.env.DB_PASSWORD || 'betka',
        port: parseInt(process.env.DB_PORT, 10) || 5432,
    });

    const client = await pool.connect();

    try {
        await client.query('BEGIN');

        const existingAtlas = await client.query('SELECT id, je_vychozi FROM atlases WHERE id = $1', [atlasData.id]);
        const atlasAlreadyExists = existingAtlas.rows.length > 0;
        const existingAnnotationCount = atlasAlreadyExists ? await countAtlasAnnotations(client, atlasData.id) : 0;

        if (atlasAlreadyExists && existingAnnotationCount > 0) {
            throw new Error(
                `Atlas ${atlasData.id} already exists and has ${existingAnnotationCount} annotations. Import aborted to avoid deleting live annotation data.`
            );
        }

        const shouldBeDefault = atlasData.id === DEFAULT_ATLAS_ID || existingAtlas.rows[0]?.je_vychozi === true;
        await upsertAtlasMetadata(client, atlasData, shouldBeDefault);
        const counts = await replaceAtlasSchemaTree(client, atlasData);

        await client.query('COMMIT');

        console.log(
            JSON.stringify(
                {
                    atlasId: atlasData.id,
                    file: path.basename(atlasFilePath),
                    action: atlasAlreadyExists ? 'replaced-schema-tree' : 'inserted-new-atlas',
                    categories: counts.categoryCount,
                    traits: counts.traitCount,
                    options: counts.optionCount,
                },
                null,
                2
            )
        );
    } catch (error) {
        await client.query('ROLLBACK');
        throw error;
    } finally {
        client.release();
        await pool.end();
    }
}

if (require.main === module) {
    importAtlas(process.argv[2])
        .catch((error) => {
            console.error(`❌ Atlas import failed: ${error.message}`);
            process.exit(1);
        });
}

module.exports = {
    getAtlasTranslationFilePath,
    importAtlas,
    partitionOptionImages,
    readAtlasJson,
    resolveAtlasFilePath,
    toImportedTraitId,
};