import { marked } from 'marked';

import type { key_val } from '$lib/stores/ae_stores';
import { api } from '$lib/ae_api/api';

import { db_save_ae_obj_li__ae_obj } from '$lib/ae_core/core__idb_dexie';
import { build_tmp_sort } from '$lib/ae_core/core__idb_sort';
import { db_journals } from '$lib/ae_journals/db_journals';
import type { ae_Journal } from '$lib/types/ae_types';

import { load_ae_obj_li__journal_entry } from '$lib/ae_journals/ae_journals__journal_entry';

const ae_promises: key_val = {};

// Updated 2026-01-26 (SWR Optimization)
export async function load_ae_obj_id__journal({
    api_cfg,
    journal_id,
    inc_entry_li = false,
    enabled = 'enabled',
    hidden = 'not_hidden',
    view = 'default',
    limit = 99,
    offset = 0,
    order_by_li = {
        priority: 'DESC',
        sort: 'DESC',
        name: 'ASC',
        updated_on: 'DESC',
        created_on: 'DESC'
    },
    params = {},
    try_cache = true,
    log_lvl = 0
}: {
    api_cfg: any;
    journal_id: string;
    inc_entry_li?: boolean;
    enabled?: 'enabled' | 'all' | 'not_enabled' | undefined; // all, disabled, enabled
    hidden?: 'hidden' | 'all' | 'not_hidden' | undefined; // all, hidden, not_hidden
    view?: string;
    limit?: number;
    offset?: number;
    order_by_li?: key_val; // Order by fields for the journal entries
    params?: key_val;
    try_cache?: boolean;
    log_lvl?: number;
}): Promise<ae_Journal | null> {
    if (log_lvl) {
        console.log(
            `*** load_ae_obj_id__journal() *** journal_id=${journal_id} (SWR)`
        );
    }

    // 1. FAST PATH: Return cached data immediately
    if (try_cache) {
        try {
            const cached = await db_journals.journal.get(journal_id);
            if (cached) {
                if (log_lvl)
                    console.log(
                        'JOURNAL LOAD: Cache hit. Returning stale data.'
                    );
                _refresh_journal_id_background({
                    api_cfg,
                    journal_id,
                    view,
                    params,
                    try_cache,
                    inc_entry_li,
                    enabled,
                    hidden,
                    limit,
                    offset,
                    order_by_li,
                    log_lvl: 0
                });
                if (inc_entry_li && !cached.journal_entry_li) {
                    cached.journal_entry_li =
                        await load_ae_obj_li__journal_entry({
                            api_cfg,
                            for_obj_type: 'journal',
                            for_obj_id: journal_id,
                            enabled,
                            hidden,
                            limit,
                            offset,
                            order_by_li,
                            params,
                            try_cache,
                            log_lvl
                        });
                }
                return cached;
            }
        } catch (e) {}
    }

    // 2. SLOW PATH: Wait for API
    return await _refresh_journal_id_background({
        api_cfg,
        journal_id,
        view,
        params,
        try_cache,
        inc_entry_li,
        enabled,
        hidden,
        limit,
        offset,
        order_by_li,
        log_lvl
    });
}

/**
 * Internal background refresh for a single journal
 */
async function _refresh_journal_id_background({
    api_cfg,
    journal_id,
    view,
    params,
    try_cache,
    inc_entry_li,
    enabled,
    hidden,
    limit,
    offset,
    order_by_li,
    log_lvl
}: any) {
    if (typeof navigator !== 'undefined' && !navigator.onLine) return null;
    try {
        const result = await api.get_ae_obj({
            api_cfg,
            obj_type: 'journal',
            obj_id: journal_id,
            view,
            params,
            log_lvl
        });
        if (result) {
            if (try_cache) {
                const processed = await process_ae_obj__journal_props({
                    obj_li: [result],
                    log_lvl
                });
                // IDB write is optional caching — quota failures must not discard the API result.
                try {
                    await db_save_ae_obj_li__ae_obj({
                        db_instance: db_journals,
                        table_name: 'journal',
                        obj_li: processed,
                        properties_to_save,
                        log_lvl
                    });
                } catch (save_error) {
                    console.warn('IDB cache write failed for journal (quota?):', save_error);
                }
                // Yield to microtask queue so Dexie liveQuery observers fire before we return
                await Promise.resolve();
            }
            if (inc_entry_li) {
                result.journal_entry_li = await load_ae_obj_li__journal_entry({
                    api_cfg,
                    for_obj_type: 'journal',
                    for_obj_id: journal_id,
                    enabled,
                    hidden,
                    limit,
                    offset,
                    order_by_li,
                    params,
                    try_cache,
                    log_lvl
                });
            }
            return result;
        }
    } catch (e) {}
    return null;
}

// Updated 2026-01-26 (SWR Optimization)
export async function load_ae_obj_li__journal({
    api_cfg,
    for_obj_type = 'account',
    for_obj_id,
    qry_person_id = null,
    inc_entry_li = false,
    enabled = 'enabled',
    hidden = 'not_hidden',
    limit = 99,
    offset = 0,
    order_by_li = {
        priority: 'DESC',
        sort: 'DESC',
        name: 'ASC',
        updated_on: 'DESC',
        created_on: 'DESC'
    },
    params = {},
    try_cache = true,
    log_lvl = 0
}: {
    api_cfg: any;
    for_obj_type: string;
    for_obj_id: string;
    qry_person_id?: string | null;
    inc_entry_li?: boolean;
    enabled?: 'enabled' | 'all' | 'not_enabled' | undefined; // all, disabled, enabled
    hidden?: 'hidden' | 'all' | 'not_hidden' | undefined; // all, hidden, not_hidden
    limit?: number;
    offset?: number;
    order_by_li?: key_val; // Order by fields for the journal entries
    params?: key_val;
    try_cache?: boolean;
    log_lvl?: number;
}): Promise<ae_Journal[]> {
    if (log_lvl) {
        console.log(
            `*** load_ae_obj_li__journal() *** for=${for_obj_type}:${for_obj_id} (SWR)`
        );
    }

    // 1. FAST PATH: Check cache
    if (try_cache) {
        try {
            const cached_li = await db_journals.journal
                .where('for_id')
                .equals(for_obj_id)
                .toArray();
            if (cached_li && cached_li.length > 0) {
                if (log_lvl)
                    console.log(
                        `JOURNAL LIST: Cache hit (${cached_li.length}).`
                    );
                _refresh_journal_li_background({
                    api_cfg,
                    for_obj_type,
                    for_obj_id,
                    qry_person_id,
                    inc_entry_li,
                    enabled,
                    hidden,
                    limit,
                    offset,
                    order_by_li,
                    params,
                    try_cache,
                    log_lvl: 0
                });
                return cached_li;
            }
        } catch (e) {}
    }

    // 2. SLOW PATH: API
    return await _refresh_journal_li_background({
        api_cfg,
        for_obj_type,
        for_obj_id,
        qry_person_id,
        inc_entry_li,
        enabled,
        hidden,
        limit,
        offset,
        order_by_li,
        params,
        try_cache,
        log_lvl
    });
}

async function _refresh_journal_li_background({
    api_cfg,
    for_obj_type,
    for_obj_id,
    qry_person_id,
    inc_entry_li,
    enabled,
    hidden,
    limit,
    offset,
    order_by_li,
    params,
    try_cache,
    log_lvl
}: any) {
    if (typeof navigator !== 'undefined' && !navigator.onLine) return [];

    let promise;
    if (qry_person_id) {
        const search_query: any = {
            and: [{ field: 'person_id', op: 'eq', value: qry_person_id }]
        };
        if (for_obj_id)
            search_query.and.push({
                field: `${for_obj_type}_id`,
                op: 'eq',
                value: for_obj_id
            });
        if (enabled === 'enabled')
            search_query.and.push({ field: 'enable', op: 'eq', value: true });
        if (hidden === 'hidden')
            search_query.and.push({ field: 'hide', op: 'eq', value: true });

        promise = api.search_ae_obj({
            api_cfg,
            obj_type: 'journal',
            search_query,
            order_by_li,
            limit,
            offset,
            log_lvl
        });
    } else {
        promise = api.get_ae_obj_li({
            api_cfg,
            obj_type: 'journal',
            for_obj_type,
            for_obj_id,
            enabled,
            hidden,
            limit,
            offset,
            order_by_li,
            log_lvl
        });
    }

    try {
        const results = await promise;
        if (results) {
            if (try_cache) {
                const processed = await process_ae_obj__journal_props({
                    obj_li: results,
                    log_lvl
                });
                // IDB write is optional caching — quota failures must not discard the API result.
                try {
                    await db_save_ae_obj_li__ae_obj({
                        db_instance: db_journals,
                        table_name: 'journal',
                        obj_li: processed,
                        properties_to_save,
                        log_lvl
                    });
                } catch (save_error) {
                    console.warn('IDB cache write failed for journal (quota?):', save_error);
                }
                // Yield to microtask queue so Dexie liveQuery observers fire before we return
                await Promise.resolve();
            }
            if (inc_entry_li) {
                for (const journal of results) {
                    load_ae_obj_li__journal_entry({
                        api_cfg,
                        for_obj_type: 'journal',
                        for_obj_id: journal.journal_id,
                        enabled,
                        hidden,
                        limit,
                        offset,
                        order_by_li,
                        params,
                        try_cache,
                        log_lvl: 0
                    });
                }
            }
            return results;
        }
    } catch (e) {}
    return [];
}

// Updated 2026-01-05
export async function create_ae_obj__journal({
    api_cfg,
    account_id,
    data_kv,
    params = {},
    try_cache = true,
    log_lvl = 0
}: {
    api_cfg: any;
    account_id: string;
    data_kv: key_val;
    params?: key_val;
    try_cache?: boolean;
    log_lvl?: number;
}): Promise<ae_Journal | null> {
    if (log_lvl) {
        console.log(
            `*** create_ae_obj__journal() *** account_id=${account_id}`
        );
    }

    if (!account_id) {
        console.log(
            `ERROR: Journals - Journal - account_id required to create`
        );
        return null;
    }

    ae_promises.create__journal = await api
        .create_ae_obj({
            api_cfg: api_cfg,
            obj_type: 'journal',
            fields: {
                account_id: account_id,
                ...data_kv
            },
            params: params,
            log_lvl: log_lvl
        })
        .then(async function (journal_obj_create_result) {
            if (journal_obj_create_result) {
                if (try_cache) {
                    // Process the results first
                    const processed_obj_li =
                        await process_ae_obj__journal_props({
                            obj_li: [journal_obj_create_result],
                            log_lvl: log_lvl
                        });
                    if (log_lvl) {
                        console.log('Processed object list:', processed_obj_li);
                    }
                    // IDB write is optional caching — quota failures must not discard the API result.
                    try {
                        await db_save_ae_obj_li__ae_obj({
                            db_instance: db_journals,
                            table_name: 'journal',
                            obj_li: processed_obj_li,
                            properties_to_save: properties_to_save,
                            log_lvl: log_lvl
                        });
                    } catch (save_error) {
                        console.warn('IDB cache write failed for journal (quota?):', save_error);
                    }
                }
                return journal_obj_create_result;
            } else {
                return null;
            }
        })
        .catch(function (error: any) {
            console.log('No results returned or failed.', error);
        });

    return ae_promises.create__journal;
}

// Updated 2026-01-05
export async function delete_ae_obj_id__journal({
    api_cfg,
    journal_id,
    method = 'delete', // 'delete', 'disable', 'hide'
    params = {},
    try_cache = true,
    log_lvl = 0
}: {
    api_cfg: any;
    journal_id: string;
    method?: 'delete' | 'soft_delete' | 'disable' | 'hide';
    params?: key_val;
    try_cache?: boolean;
    log_lvl?: number;
}) {
    if (log_lvl) {
        console.log(
            `*** delete_ae_obj_id__journal() *** journal_id=${journal_id}`
        );
    }

    ae_promises.delete__journal_obj = await api
        .delete_ae_obj({
            api_cfg: api_cfg,
            obj_type: 'journal',
            obj_id: journal_id,
            params: params,
            method: method,
            log_lvl: log_lvl
        })
        .catch(function (error: any) {
            console.log('No results returned or failed.', error);
        })
        .finally(async function () {
            if (try_cache) {
                if (log_lvl) {
                    console.log(
                        `Attempting to remove IDB entry for journal_id=${journal_id}`
                    );
                }
                await db_journals.journal.delete(journal_id);
            }
        });

    return ae_promises.delete__journal_obj;
}

// Updated 2026-01-05
export async function update_ae_obj__journal({
    api_cfg,
    journal_id,
    data_kv,
    params = {},
    try_cache = true,
    log_lvl = 0
}: {
    api_cfg: any;
    journal_id: string;
    data_kv: key_val;
    params?: key_val;
    try_cache?: boolean;
    log_lvl?: number;
}): Promise<ae_Journal | null> {
    if (log_lvl) {
        console.log(
            `*** update_ae_obj__journal() *** journal_id=${journal_id}`,
            data_kv
        );
    }

    // Perform the API update
    const result = await api.update_ae_obj({
        api_cfg: api_cfg,
        obj_type: 'journal',
        obj_id: journal_id,
        fields: data_kv,
        params: params,
        log_lvl: log_lvl
    });

    // Handle the result
    if (result) {
        if (try_cache) {
            // Process the results first
            const processed_obj_li = await process_ae_obj__journal_props({
                obj_li: [result],
                log_lvl: log_lvl
            });
            if (log_lvl) {
                console.log('Processed object list:', processed_obj_li);
            }
            // IDB write is optional caching — quota failures must not discard the API result.
            try {
                await db_save_ae_obj_li__ae_obj({
                    db_instance: db_journals,
                    table_name: 'journal',
                    obj_li: processed_obj_li,
                    properties_to_save: properties_to_save,
                    log_lvl: log_lvl
                });
            } catch (save_error) {
                console.warn('IDB cache write failed for journal (quota?):', save_error);
            }
        }
        return result;
    } else {
        console.error('Failed to update journal.');
        return null;
    }
}

// This new function is using CRUD V3 Search.
// Updated 2026-01-02
export async function qry__journal({
    api_cfg,
    journal_id,
    qry_str,
    qry_files,
    qry_start_datetime, // Example greater than: '2024-10-24'
    enabled = 'enabled',
    hidden = 'not_hidden',
    limit = 50,
    offset = 0,
    order_by_li = {
        priority: 'DESC',
        sort: 'DESC',
        start_datetime: 'ASC',
        name: 'ASC',
        updated_on: 'DESC',
        created_on: 'DESC'
    } as const,
    params = {},
    try_cache = true,
    log_lvl = 0
}: {
    api_cfg: any;
    journal_id: any;
    qry_str?: string;
    qry_files?: null | boolean;
    qry_start_datetime?: null | string; // Greater than this datetime
    enabled?: 'enabled' | 'all' | 'not_enabled' | undefined; // all, disabled, enabled
    hidden?: 'hidden' | 'all' | 'not_hidden' | undefined; // all, hidden, not_hidden
    limit?: number;
    offset?: number;
    order_by_li?:
        | Record<string, 'ASC' | 'DESC'>
        | Record<string, 'ASC' | 'DESC'>[];
    params?: any;
    try_cache?: boolean;
    log_lvl?: number;
}): Promise<ae_Journal[]> {
    if (log_lvl) {
        console.log(
            `*** qry__journal() *** journal_id=${journal_id} enabled=${enabled} hidden=${hidden} limit=${limit} offset=${offset}`
        );
    }

    const search_query: any = { and: [] };

    if (qry_files === true) {
        search_query.and.push({ field: 'file_count_all', op: 'gt', value: 0 });
    } else if (qry_files === false) {
        search_query.and.push({
            field: 'file_count_all',
            op: 'is',
            value: null
        });
    }

    if (qry_start_datetime) {
        search_query.and.push({
            field: 'start_datetime',
            op: 'gt',
            value: qry_start_datetime
        });
    }

    // Add for_obj_id context (Account ID)
    if (journal_id) {
        // Assuming journal_id here is actually the account_id as per original usage context
        search_query.and.push({
            field: 'account_id',
            op: 'eq',
            value: journal_id
        });
    }

    // Add enabled/hidden filters
    if (enabled === 'enabled') {
        search_query.and.push({ field: 'enable', op: 'eq', value: true });
    } else if (enabled === 'not_enabled') {
        search_query.and.push({ field: 'enable', op: 'eq', value: false });
    }

    if (hidden === 'hidden') {
        search_query.and.push({ field: 'hide', op: 'eq', value: true });
    } else if (hidden === 'not_hidden') {
        search_query.and.push({ field: 'hide', op: 'eq', value: false });
    }

    ae_promises.load__journal_obj_li = await api
        .search_ae_obj({
            api_cfg: api_cfg,
            obj_type: 'journal',
            search_query,
            order_by_li,
            limit,
            offset,
            log_lvl
        })
        .then(async function (journal_obj_li_get_result) {
            if (journal_obj_li_get_result) {
                if (try_cache) {
                    // Process the results first
                    const processed_obj_li =
                        await process_ae_obj__journal_props({
                            obj_li: journal_obj_li_get_result,
                            log_lvl: log_lvl
                        });
                    if (log_lvl) {
                        console.log('Processed object list:', processed_obj_li);
                    }
                    // Save the updated results list to the database
                    if (log_lvl) {
                        console.log('Saving to DB...');
                    }
                    db_save_ae_obj_li__ae_obj({
                        db_instance: db_journals,
                        table_name: 'journal',
                        obj_li: processed_obj_li,
                        properties_to_save: properties_to_save,
                        log_lvl: log_lvl
                    });
                    if (log_lvl) {
                        console.log('DB save completed.');
                    }
                }
                return journal_obj_li_get_result;
            } else {
                return [];
            }
        });

    if (log_lvl) {
        console.log(
            'ae_promises.load__journal_obj_li:',
            ae_promises.load__journal_obj_li
        );
    }
    return ae_promises.load__journal_obj_li;
}

// Updated 2025-05-09
const properties_to_save = [
    'id',
    'journal_id',

    'code',

    'for_type',
    'for_id',
    'type_code',

    'account_id',
    'person_id',

    'name',
    'short_name',
    'summary',
    'outline',

    'description',
    // description_md_html is computed from description on every load — not stored to save IDB quota
    'description_html',
    'description_json',

    // start_datetime: obj.start_datetime,
    // end_datetime: obj.end_datetime,
    'timezone',

    'alert',
    'alert_msg',

    'sort_by',
    'sort_by_desc',

    'cfg_json',

    // ux_mode: obj.ux_mode,

    // This only allows for basic access to the data.
    'passcode_read', // For LLM (AI) generated summary...???
    'passcode_read_expire',
    'passcode_write',
    'passcode_write_expire',

    'passcode', // For Journal Entry encryption password
    'passcode_timeout',

    'private_passcode', // Combine with Journal passcode to encrypt and decrypt Entries

    'auth_key', // For Journal authorization without sign in

    'enable',
    'hide',
    'priority',
    'sort',
    'group',
    'notes',
    'created_on',
    'updated_on',

    // Generated fields for sorting locally only
    'tmp_sort_1',
    'tmp_sort_2',
    'tmp_sort_3',
    // tmp_sort_1: `${obj.original_datetime}_${obj.group}_${obj.priority}_${obj.sort}`,
    // tmp_sort_2: `${obj.group}_${obj.original_datetime}_${obj.priority}_${obj.sort}`,
    // 'tmp_sort_a',
    // 'tmp_sort_b',

    'combined_passcode',

    // From SQL view
    'journal_entry_count'

    // A key value list of the others
    // journal_other_kv
    // journal_other_li
];

/**
 * NON-EXPORTED LOCAL HELPER
 * Processes a list of Aether objects by applying common and specific transformations.
 */
async function _process_generic_props<T extends Record<string, any>>({
    obj_li,
    obj_type,
    log_lvl = 0,
    specific_processor
}: {
    obj_li: T[];
    obj_type: string;
    log_lvl?: number;
    specific_processor?: (obj: T) => Promise<T> | T;
}): Promise<T[]> {
    if (log_lvl > 0) {
        console.log(
            `*** _process_generic_props: Processing ${obj_li.length} objects of type "${obj_type}" ***`
        );
    }

    if (!obj_li || obj_li.length === 0) {
        if (log_lvl > 0) console.log('No objects to process.');
        return [];
    }

    const processed_obj_li: T[] = [];

    for (const original_obj of obj_li) {
        let processed_obj = { ...original_obj };

        // --- Common Transformations ---

        // 1. Standardize ID and other '_random' fields
        for (const key in processed_obj) {
            if (key.endsWith('_random')) {
                const newKey = key.slice(0, -7); // Remove '_random' suffix
                (processed_obj as any)[newKey] = processed_obj[key];
            }
        }
        // Ensure 'id' is set from '[obj_type]_id_random'
        const randomIdKey = `${obj_type}_id`;
        if (processed_obj[randomIdKey]) {
            (processed_obj as any).id = processed_obj[randomIdKey];
        }

        // 2. Create common computed properties for client-side sorting.
        const group = processed_obj.group ?? '0';
        const priority = processed_obj.priority ? 1 : 0;
        const sort = processed_obj.sort ?? '0';
        const updated =
            processed_obj.updated_on ??
            processed_obj.created_on ??
            new Date(0).toISOString();
        const name = processed_obj.name ?? '';

        (processed_obj as any).tmp_sort_1 =
            `${group}_${priority}_${sort}_${updated}`;
        (processed_obj as any).tmp_sort_2 =
            `${group}_${priority}_${sort}_${name}_${updated}`;

        // --- Specific Transformations ---
        if (specific_processor) {
            processed_obj = await Promise.resolve(
                specific_processor(processed_obj)
            );
        }

        processed_obj_li.push(processed_obj as T);
    }

    return processed_obj_li;
}

// Updated 2025-11-13
export async function process_ae_obj__journal_props({
    obj_li,
    log_lvl = 0
}: {
    obj_li: any[];
    log_lvl?: number;
}) {
    return _process_generic_props({
        obj_li,
        obj_type: 'journal',
        log_lvl,
        specific_processor: async (obj) => {
            const description = obj.description ?? '';
            const description_cleaned: string = description.replace(
                /^[\u200B\u200C\u200D\u200E\u200F\uFEFF]/,
                ''
            );
            obj.description_md_html =
                (await marked.parse(description_cleaned ?? '')) ?? null;

            obj.cfg_json = obj.cfg_json ?? {};
            obj.data_json = obj.data_json ?? {};

            const updated =
                obj.updated_on ?? obj.created_on ?? new Date(0).toISOString();
            const { tmp_sort_1, tmp_sort_2, tmp_sort_3 } = build_tmp_sort({
                prefix: [obj.group ?? '0'],
                priority: obj.priority,
                sort: obj.sort,
                fields_2: [obj.name],
                fields_3: [updated]
            });
            obj.tmp_sort_1 = tmp_sort_1;
            obj.tmp_sort_2 = tmp_sort_2;
            obj.tmp_sort_3 = tmp_sort_3;
            obj.combined_passcode = `${obj.passcode ?? ''}:${obj.private_passcode ?? ''}`;

            return obj;
        }
    });
}
