<script lang="ts">
import { run } from 'svelte/legacy';

import { createEventDispatcher, onMount, tick } from 'svelte';

import type { key_val } from '$lib/stores/ae_stores';
import { ae_util } from '$lib/ae_utils/ae_utils';
// import { api } from '$lib/api';
import { check_hosted_file_obj_w_hash } from '$lib/ae_core/core__check_hosted_file_obj_w_hash';
import {
    ae_loc,
    ae_sess,
    ae_api,
    ae_trig,
    slct,
    slct_trigger
} from '$lib/stores/ae_stores';
import { LoaderCircle, Minus, Upload } from '@lucide/svelte';
const dispatch = createEventDispatcher();

interface Props {
    element_id?: string;
    input_name?: string;
    container_class_li?: string[];
    input_class_li?: string[];
    table_class_li?: string[];
    multiple?: boolean;
    required?: boolean;
    accept?: string;
    untrusted_extension_list?: any;
    legacy_extension_list?: any;
    use_selected_file_table?: boolean;
    file_list_status?: null | string;
    processed_file_list?: any[];
    input_file_list?: any;
}

let {
    element_id = 'svelte_input_file_element',
    input_name = 'file_list',
    container_class_li = [],
    input_class_li = ['file_drop_area'],
    table_class_li = ['table', 'table-sm', 'table-striped', '', 'text-sm'],
    multiple = true,
    required = true,
    accept = 'audio/*, image/*, video/*, .bak, .cfg, .css, .csv, .doc, .docx, .gz, .htm, .html, .ini, .iso, .j2, .json, .key, .keynote, .md, .pdf, .ppt, .pptx, .rar, .rtf, .sql, .svelte, ttf, .txt, .xls, .xlsx, .xz, .zip, .bin, .dmg, .exe, .js, .msi, .php, .py, .sh',
    untrusted_extension_list = [
        'bin',
        'dmg',
        'exe',
        'js',
        'msi',
        'php',
        'py',
        'sh'
    ],
    legacy_extension_list = ['avi', 'doc', 'ppt', 'xls', 'wmv'],
    use_selected_file_table = true,
    file_list_status = $bindable(null),
    processed_file_list = $bindable([]),
    input_file_list = $bindable(null)
}: Props = $props();
let input_file_list_processed: any[] = $state([]);

onMount(() => {
    console.log('** Element Mounted: ** Element Input File');
});

function prevent_default<T extends Event>(fn: (event: T) => void) {
    return function (event: T) {
        event.preventDefault();
        fn(event);
    };
}

async function process_file_list(file_list: FileList | null) {
    console.log('*** process_file_list() ***');

    file_list_status = 'processing';
    processed_file_list = [];

    if (!file_list) {
        // file_list_processed = null;
        file_list_status = 'none';
        // await tick();
        return processed_file_list;
    }

    // const forLoop = async _ => {
    // console.log('*** Start ***');

    // for (let [i, file_item] of file_list.entries()) { //  Not sure why this does not work???
    for await (const [i, file_item] of Array.prototype.entries.call(
        file_list
    )) {
        console.log(i, file_item);

        // NOTE: The file list is readonly. The filenames can not be changed here.
        if (
            file_item.name.endsWith('.odpmac') ||
            file_item.name.endsWith('.odpwin') ||
            file_item.name.endsWith('.pptmac') ||
            file_item.name.endsWith('.pptwin') ||
            file_item.name.endsWith('.pptxmac') ||
            file_item.name.endsWith('.pptxwin')
        ) {
            console.log(
                'This file extension may need to be fixed? API upload will take care of it.'
            );
            // file_item.name = file_item.name.replace('.odpwin', '.odp');
        }

        let file_data: key_val = {};

        let filename = file_item.name;
        // console.log(filename);
        file_data['filename'] = filename;

        let guessed_extension = ae_util.guess_file_extension(filename);
        file_data['guessed_extension'] = guessed_extension;

        file_data['type'] = file_item.type;

        let modified_date = new Date(file_item.lastModified);
        file_data['modified_date'] = modified_date;
        let modified_datetime_string = ae_util.iso_datetime_formatter(
            modified_date,
            'datetime_medium'
        );
        file_data['modified_datetime_string'] = modified_datetime_string;

        let file_size_bytes = file_item.size;
        file_data['file_size_bytes'] = file_size_bytes;
        let file_size_string = ae_util.format_bytes(file_item.size, 2);
        file_data['file_size_string'] = file_size_string;

        // // NOTE: Calculate the hash of the file before upload. Check if this exact file has already been uploaded.
        // let file_reader = new FileReader();
        // file_reader.onload = async function() {

        //     const hash_buffer = crypto.subtle.digest('SHA-256', file_reader.result);
        //     let hash_hex_test = hash_buffer.then(async function (result_buffer) {
        //         const hash_array = Array.from(new Uint8Array(result_buffer)); // convert buffer to byte array
        //         const hash_hex = hash_array.map(b => b.toString(16).padStart(2, '0')).join('');
        //         console.log(`File hash hex? ${hash_hex}`);
        //         file_data['hash_sha256'] = hash_hex;
        //         return hash_hex;
        //     })
        //     .catch(function (error: any) {
        //         console.log('Something went wrong?', error);
        //     });

        //     return hash_hex_test;

        //     // file_data['hash_hex_test'] = hash_hex_test;

        //     // const hash_buffer = await crypto.subtle.digest('SHA-256', file_reader.result);
        //     // let hash_str = await new TextDecoder().decode(hash_buffer);
        //     // console.log(`File hash string? ${hash_str}`);
        //     // file_data['hash_str'] = hash_str;

        //     // const hash_array = Array.from(new Uint8Array(hash_buffer)); // convert buffer to byte array
        //     // const hash_hex = hash_array.map(b => b.toString(16).padStart(2, '0')).join('');
        //     // console.log(`File hash hex? ${hash_hex}`);

        //     // file_data['hash_sha256'] = hash_hex;
        //     // return hash_hex_test;
        //     // return hash_hex;
        // }

        // // file_reader.then(function (result) {
        // //     console.log(`File hash hex? ${result}`);
        // //     return hash_hex;
        // // })
        // // .catch(function (error: any) {
        // //     console.log('No results returned or failed.', error);
        // // });
        // // file_data['hash_sha256'] = file_reader.readAsArrayBuffer(file_item);

        let warning_untrusted_extension = false;
        let warning_legacy_extension = false;
        let warning_size = false;
        let warning_message = null;

        if (untrusted_extension_list.includes(guessed_extension)) {
            console.log('This is an untrusted extension. Going to warn.');
            warning_untrusted_extension = true;
            warning_message =
                'It appears this an untrusted file type and is likely meant to be an executable or installable application. It is <strong>strongly</strong> recommended that this file not be used.';
        } else if (legacy_extension_list.includes(guessed_extension)) {
            console.log('This is a legacy extension. Going to warn.');
            warning_legacy_extension = true;
            if (guessed_extension == 'ppt') {
                warning_message =
                    'It appears this is a legacy PowerPoint file and has not been officially supported since Office PowerPoint 2003. This file is known to have issues and may not work well. It is <strong>strongly</strong> recommended that this file be saved using the modern PPTX format.';
            } else if (guessed_extension == 'avi') {
                warning_message =
                    'It appears this is a video file using the AVI format. It is <strong>strongly</strong> recommended that this file be re-saved as an MP4, MOV, MKV, or MPG/MPEG. The file will also likely be much smaller.';
            } else if (guessed_extension == 'wmv') {
                warning_message =
                    "It appears this is a video file using Microsoft's WMV format. It is <strong>strongly</strong> recommended that this file be re-saved as an MP4, MOV, MKV, or MPG/MPEG.";
            } else {
                warning_message =
                    'It appears this is a legacy or not very well supported file format. It is <strong>strongly</strong> recommended that it be saved in an alternative format if possible.';
            }
        } else if (file_size_bytes > 52428800) {
            // 50 MB = 52428800 bytes
            // 100 MB = 104857600 bytes
            console.log(
                `This is a large file size ${file_size_bytes / 1048576} MB (${file_size_bytes} bytes). Going to warn.`
            );
            warning_size = true;
            if (file_size_bytes > 2147483648) {
                // > 2 GB
                warning_message = `This file size (${file_size_string}) is very large and will take at <strong>least</strong> a few minutes to upload depending on your network connection. In some cases it may be worth compressing the file or embedded media. Most audio, image, and video files can be compressed without a significant loss in quality. Be sure you have a stable network connection, especially if you are uploading over a wireless connection. Many business (convention centers, hotels, restaurants, etc) cap upload speeds significantly.`;
            } else if (file_size_bytes > 209715200) {
                // > 200 MB
                warning_message = `This file size (${file_size_string}) is large and will likely take at <strong>least</strong> a few minutes to upload depending on your network connection. Be sure you have a stable network connection, especially if you are uploading over a wireless connection. Many business (convention centers, hotels, restaurants, etc) cap upload speeds significantly. In some cases it may be worth compressing the file or embedded media. Many audio, image, and video files can be compressed without a significant loss in quality.`;
            } else if (file_size_bytes > 104857600) {
                // 100 MB to 200 MB
                warning_message = `This file size (${file_size_string}) is large and will likely take a few minutes to upload depending on your network connection. Be sure you have a stable network connection. In some cases it may be worth compressing the file or embedded media. Many audio, image, and video files can be compressed without a significant loss in quality.`;
            } else {
                // 50 to 100 MB
                warning_message = `This file size (${file_size_string}) is large and may take a few minutes to upload depending on your network connection. In some cases it may be worth compressing the file or embedded media. Many audio, image, and video files can be compressed without a significant loss in quality.`;
            }
        } else {
        }

        file_data['warning_untrusted_extension'] = warning_untrusted_extension;
        file_data['warning_legacy_extension'] = warning_legacy_extension;
        file_data['warning_size'] = warning_size;

        file_data['warning_message'] = warning_message;

        file_data['uploaded'] = null;
        file_data['uploaded_bytes'] = null;

        // input_file_list_processed.push(JSON.parse(JSON.stringify(file_data)));
        // input_file_list_processed = input_file_list_processed;

        // console.log(get_file_hash(file_item));
        // console.log(await get_file_hash(file_item));

        let file_hash = null;

        // Only hash files less than 2 GB (2147483648 bytes)!!!
        console.log(
            `File size: ${file_size_bytes / 1048576} MB (${file_size_bytes} bytes)`
        );
        if (file_size_bytes < 2000000000) {
            // > 2 GB 2 147 483 648
            file_hash = await ae_util.get_file_hash(file_item);
        } else {
            // File size in MB
            console.log(
                `File is too large to hash. File size: ${file_size_bytes / 1048576} MB`
            );
        }

        if (file_hash) {
            console.log(
                `Found file hash to lookup: ${ae_util.shorten_string({ string: file_hash })}`
            );
            file_data['hash_sha256'] = file_hash;

            let check_hosted_file_obj_w_hash_result =
                await check_hosted_file_obj_w_hash({
                    api_cfg: $ae_api,
                    hosted_file_hash: file_hash
                });

            // console.log(check_hosted_file_obj_w_hash_result);

            if (
                check_hosted_file_obj_w_hash_result &&
                check_hosted_file_obj_w_hash_result.hosted_file_found_check
            ) {
                console.log('Matching hash!!!');
                file_data['hash_sha256_match'] = true;
                // $ae_events.pres_mgmt.new_upload_list[i].hash_sha256_match = true;
                // $ae_events = $ae_events;
            }
        } else {
            file_data['hash_sha256'] = null;
            file_data['hash_sha256_match'] = false;
        }

        processed_file_list.push(file_data);
        // input_file_list_processed.push(file_data);
    }

    file_list_status = 'ready';
    console.log(processed_file_list);

    // return JSON.parse(JSON.stringify(processed_file_list));
    return processed_file_list;
}

function remove_file_from_filelist(index: number) {
    console.log('*** remove_file_from_filelist() ***');

    // Can not use something like this because it is readonly:
    const dt = new DataTransfer();
    // let input = document.getElementById(input_element_id);

    let input_element = document.querySelector(
        'input[type="file"].svelte_input_file_element'
    ) as HTMLInputElement;

    if (!input_element) {
        console.error('Could not find the input element.');
        return false;
    }

    let files = input_file_list;

    if (!files || !files.length) {
        console.error('No files found in the file list.');
        file_list_status = null;
        return false;
    }

    for (let i = 0; i < files.length; i++) {
        const file = files[i];
        if (index !== i)
            // Only include the file if it does not match the index value.
            dt.items.add(file);
    }

    // NOTE: I thought just setting the input_element.files OR input_file_list would trigger the input_file_list change.
    // input_element.files = Object.assign({}, dt.files);
    input_element.files = dt.files; // Assign the updates list
    // input_file_list = null;
    input_file_list = dt.files; // I feel like this should not need to be done, but doing it anyways.

    return true;
}
run(() => {
    if (input_file_list) {
        console.log(input_file_list);

        process_file_list(input_file_list).then(function (result) {
            // console.log(result);

            if (!result || !result.length) {
                file_list_status = 'none';
            }

            // Save the results to the file upload list to be displayed as a table.
            input_file_list_processed = result; // Includes file hash

            dispatch('input_file_list_updated', {
                element_id: element_id,
                input_file_list: input_file_list,
                input_file_list_processed: result // Includes file hash
            });
        });
    }
});
</script>

<div
    class="svelte_element ae_element ae_input_file flex flex-col items-center justify-center gap-1 {container_class_li.join(
        ' '
    )} text-center">
    <label for={element_id} class="svelte_input_file_label text-center">
        <div>
            <Upload size="1em" />
            <!-- Select files to upload -->
            <!-- <span class="fas fa-file-archive"></span> -->
            <strong>Upload your files</strong>
            <!-- (drag and drop) -->
        </div>
        <span>
            Presentation related files only<br />
            (PowerPoint, Keynote, PDF, mp4, Word Doc, Excel, txt, etc)
        </span>
    </label>

    <input
        id={element_id}
        type="file"
        bind:files={input_file_list}
        {multiple}
        {required}
        {accept}
        name={input_name}
        class="svelte_input_file_element {input_class_li.join(' ')}" />

    {#if file_list_status == 'processing'}
        <div
            class="file_list_status ae_warning preset-tonal-warning border-warning-500 m-1 border p-1">
            <LoaderCircle size="1em" class="m-1 animate-spin" /> Processing selected
            file list...
        </div>
    {/if}

    <!-- {#await input_file_list_processed} -->
    <!-- {:then} -->
    {#if use_selected_file_table && input_file_list_processed && input_file_list_processed.length}
        <strong>Files selected for upload</strong>
        <table class="slct_file_list text-sm {table_class_li.join(' ')}">
            <thead>
                <tr>
                    <th>Remove</th>
                    <th>Filename</th>
                    <th>Modified</th>
                    <th>Size</th>
                    <!-- <th>Type</th> -->
                    <th>Ext</th>
                    <th>Hash</th>
                </tr>
            </thead>
            {#each input_file_list_processed as file_list_item, file_index (file_index)}
                <tbody>
                    <tr>
                        <td class="file_remove">
                            <button
                                onclick={prevent_default(() => {
                                    remove_file_from_filelist(file_index);
                                })}
                                class="btn btn-md preset-tonal-warning hover:preset-filled-secondary-500 m-1"
                                title="Remove file from upload list">
                                <Minus size="1em" />
                                <span class="hidden">Remove</span>
                            </button>
                        </td>
                        <td class="file_filename">{file_list_item.filename}</td>
                        <td class="file_last_modified"
                            >{file_list_item.modified_datetime_string}</td>
                        <td
                            class="file_size"
                            class:bg-pink-200={file_list_item.warning_size}>
                            {file_list_item.file_size_string}
                            <!-- {#if $ae_sess.api_upload_kv[link_to_id]}
                    <span class="text-xs">({$ae_sess.api_upload_kv[link_to_id].percent_completed}%)</span>
                {/if} -->
                        </td>
                        <!-- <td class="file_type" class:warning_file_untrusted_extension={file_list_item.warning_untrusted_extension} class:warning_file_legacy_extension={file_list_item.warning_legacy_extension}>{file_list_item.type}</td> -->
                        <td
                            class="file_extension"
                            class:bg-red-200={file_list_item.warning_untrusted_extension}
                            class:bg-pink-200={file_list_item.warning_legacy_extension}>
                            {file_list_item.guessed_extension}
                        </td>
                        <td
                            class="file_hash file_hash256"
                            class:bg-pink-200={file_list_item.hash_sha256_match}>
                            {ae_util.shorten_string({
                                string: file_list_item.hash_sha256,
                                begin_length: 5,
                                end_length: 4,
                                wildcard_length: 2
                            })}
                        </td>
                    </tr>
                </tbody>
            {/each}
        </table>
    {/if}
</div>

<style>
th {
    text-align: center;
    /* font-size: smaller; */
}
td {
    text-align: center;
}
.file_last_modified {
    font-size: smaller;
}

.file_hash {
    font-family: 'Courier New', Courier, monospace;
}
</style>
