<script lang="ts">
// *** Import Svelte specific
import * as Lucide from '@lucide/svelte';
import { fade } from 'svelte/transition';

// *** Import Aether specific variables and functions
import type { key_val } from '$lib/stores/ae_stores';
import { ae_util } from '$lib/ae_utils/ae_utils';
import { download_ae_obj_id__hosted_file } from '$lib/ae_core/core__hosted_files';
import { ae_loc, ae_sess, ae_api } from '$lib/stores/ae_stores';

interface Props {
    log_lvl?: number;
    hosted_file_id: null | string;
    hosted_file_obj: null | key_val;
    filename?: null | string;
    max_filename?: number;
    auto_download?: boolean;
    linked_to_type?: null | string;
    linked_to_id?: null | string;
    download_complete?: null | boolean;
    download_percent?: number;
    download_status_msg?: string;
    variant?: 'tonal' | 'filled' | 'outline' | 'ghost';
    color?:
        | 'primary'
        | 'secondary'
        | 'tertiary'
        | 'success'
        | 'warning'
        | 'error'
        | 'surface';
    show_divider?: boolean;
    show_direct_download?: boolean;
    require_auth?: boolean;
    classes?: string;
    click?: () => void | Promise<any>;
    track_click_promise?: boolean;
    label?: import('svelte').Snippet;
}

let {
    log_lvl = 0,
    hosted_file_id,
    hosted_file_obj,
    filename = $bindable(null),
    max_filename = $bindable(30),
    auto_download = true,
    linked_to_type = $bindable(null),
    linked_to_id = $bindable(null),
    download_complete = $bindable(),
    download_percent = $bindable(),
    download_status_msg = $bindable('Not started'),
    variant = 'tonal',
    color = 'primary',
    show_divider = true,
    show_direct_download = false,
    require_auth = true,
    classes = '',
    click,
    track_click_promise = true,
    label
}: Props = $props();

// Map variant/color to classes using literal strings so Tailwind can find them
const color_map: Record<string, Record<string, string>> = {
    primary: {
        tonal: 'preset-tonal-primary border border-primary-500/30 hover:preset-filled-primary-500',
        filled: 'preset-filled-primary-500 hover:preset-filled-primary-600',
        outline: 'border border-primary-500 hover:preset-tonal-primary',
        ghost: 'hover:preset-tonal-primary'
    },
    secondary: {
        tonal: 'preset-tonal-secondary border border-secondary-500/30 hover:preset-filled-secondary-500',
        filled: 'preset-filled-secondary-500 hover:preset-filled-secondary-600',
        outline: 'border border-secondary-500 hover:preset-tonal-secondary',
        ghost: 'hover:preset-tonal-secondary'
    },
    tertiary: {
        tonal: 'preset-tonal-tertiary border border-tertiary-500/30 hover:preset-filled-tertiary-500',
        filled: 'preset-filled-tertiary-500 hover:preset-filled-tertiary-600',
        outline: 'border border-tertiary-500 hover:preset-tonal-tertiary',
        ghost: 'hover:preset-tonal-tertiary'
    },
    success: {
        tonal: 'preset-tonal-success border border-success-500/30 hover:preset-filled-success-500',
        filled: 'preset-filled-success-500 hover:preset-filled-success-600',
        outline: 'border border-success-500 hover:preset-tonal-success',
        ghost: 'hover:preset-tonal-success'
    },
    warning: {
        tonal: 'preset-tonal-warning border border-warning-500/30 hover:preset-filled-warning-500',
        filled: 'preset-filled-warning-500 hover:preset-filled-warning-600',
        outline: 'border border-warning-500 hover:preset-tonal-warning',
        ghost: 'hover:preset-tonal-warning'
    },
    error: {
        tonal: 'preset-tonal-error border border-error-500/30 hover:preset-filled-error-500',
        filled: 'preset-filled-error-500 hover:preset-filled-error-600',
        outline: 'border border-error-500 hover:preset-tonal-error',
        ghost: 'hover:preset-tonal-error'
    },
    surface: {
        tonal: 'preset-tonal-surface border border-surface-500/30 hover:preset-filled-surface-500',
        filled: 'preset-filled-surface-500 hover:preset-filled-surface-600',
        outline: 'border border-surface-500 hover:preset-tonal-surface',
        ghost: 'hover:preset-tonal-surface'
    }
};

let variant_classes = $derived.by(() => {
    const base =
        'btn btn-sm lg:btn-md min-w-48 transition-all overflow-hidden px-3';
    const style = color_map[color]?.[variant] || color_map.primary.tonal;
    return `${base} ${style} ${classes}`.trim();
});

let show_filename_view = $state(true);
let status_interval: any;

$effect(() => {
    if (log_lvl) {
        console.log(
            `ae_comp__hosted_files_download_button.svelte hosted_file_id=${hosted_file_id}`,
            hosted_file_obj
        );
    }
});

let ae_promises: key_val = $state({});

$effect(() => {
    const file_id = hosted_file_obj?.hosted_file_id ?? hosted_file_id;
    if (file_id && $ae_sess?.api_download_kv[file_id]?.percent_completed) {
        download_percent = $ae_sess.api_download_kv[file_id].percent_completed;
    }
});

// Reactive timer to alternate views during active download
$effect(() => {
    const file_id = hosted_file_obj?.hosted_file_id ?? hosted_file_id;
    const is_actively_downloading =
        ae_promises[file_id] && download_complete === undefined;

    if (is_actively_downloading) {
        if (!status_interval) {
            status_interval = setInterval(() => {
                show_filename_view = !show_filename_view;
            }, 3000);
        }
    } else {
        if (status_interval) {
            clearInterval(status_interval);
            status_interval = null;
        }
        show_filename_view = true; // Default view when not downloading
    }

    return () => {
        if (status_interval) {
            clearInterval(status_interval);
            status_interval = null;
        }
    };
});

let final_filename = $derived(
    filename ?? hosted_file_obj?.filename ?? 'unknown'
);
let shortened_filename = $derived(
    ae_util.shorten_filename({
        filename: final_filename,
        max_length: max_filename
    })
);

let is_url_file = $derived.by(() => {
    const raw_filename = (hosted_file_obj?.filename ?? '').toLowerCase();
    const extension = (hosted_file_obj?.extension ?? '').toLowerCase();
    return (
        raw_filename.startsWith('http://') ||
        raw_filename.startsWith('https://') ||
        extension === 'url'
    );
});

let direct_download_url = $derived.by(() => {
    if (!show_direct_download || !hosted_file_obj) return '';
    // Use event_file endpoint when event_file_id is present (canonical per API guide §5).
    // Fall back to hosted_file endpoint for standalone hosted_file objects.
    if (hosted_file_obj.event_file_id) {
        return `${$ae_api.base_url}/v3/action/event_file/${hosted_file_obj.event_file_id}/download?filename=${ae_util.clean_filename(final_filename)}&key=${$ae_api.account_id}`;
    }
    const file_id = hosted_file_obj.hosted_file_id || hosted_file_id;
    return `${$ae_api.base_url}/v3/action/hosted_file/${file_id}/download?filename=${ae_util.clean_filename(final_filename)}&key=${$ae_api.account_id}`;
});

async function handle_click() {
    const file_id = hosted_file_obj?.hosted_file_id ?? hosted_file_id;

    // URL-backed records are intentionally not downloaded. Callers are expected
    // to provide a custom click handler that opens the URL directly.
    if (is_url_file) {
        if (click) {
            const result = click();
            if (track_click_promise && result instanceof Promise) {
                ae_promises[file_id] = result;
            }
        }
        return;
    }

    download_complete = undefined;
    download_status_msg = 'Downloading...';

    if (click) {
        const result = click();
        // If the override returns a promise, track it so the UI shows progress.
        // Launcher open flows can opt out so native status messages stay authoritative.
        if (track_click_promise && result instanceof Promise) {
            ae_promises[file_id] = result;
        }
        return;
    }

    ae_promises[file_id] = download_ae_obj_id__hosted_file({
        api_cfg: $ae_api,
        hosted_file_id: file_id,
        return_file: true,
        filename: final_filename,
        auto_download: auto_download,
        log_lvl: log_lvl
    }).then((result) => {
        if (result === null) {
            console.log('File not found (404)');
            download_complete = null;
            download_status_msg = 'File not found';
        } else if (result === false) {
            console.log(
                'Possible error with API server (check network and server status)'
            );
            download_complete = false;
            download_status_msg = 'Failed to download';
        } else {
            // console.log('File found and downloaded');
            download_complete = true;
            download_status_msg = 'File downloaded';
        }
        return result;
    });
}
</script>

{#snippet content()}
    {@const file_id = hosted_file_obj?.hosted_file_id ?? hosted_file_id}
    {#await ae_promises[file_id]}
        <div class="flex min-h-[1.5rem] w-full items-center">
            <div
                class="flex shrink-0 items-center pr-2 {show_divider
                    ? 'border-surface-500/30 mr-2 border-r'
                    : ''}">
                <Lucide.LoaderCircle class="animate-spin" size={18} />
            </div>
            <div class="relative h-full grow text-left">
                {#if show_filename_view}
                    <div
                        in:fade={{ duration: 250 }}
                        out:fade={{ duration: 250 }}
                        class="flex h-full items-center">
                        <span class="truncate">
                            {shortened_filename}
                        </span>
                    </div>
                {:else}
                    <div
                        in:fade={{ duration: 250 }}
                        out:fade={{ duration: 250 }}
                        class="absolute inset-0 flex h-full items-center">
                        <span class="font-bold whitespace-nowrap">
                            Downloading:
                            {#if $ae_sess.api_download_kv[file_id]}
                                {$ae_sess.api_download_kv[file_id]
                                    .percent_completed}%
                            {:else}
                                ...
                            {/if}
                        </span>
                    </div>
                {/if}
            </div>
        </div>
    {:then}
        {#if label}
            {@render label()}
        {:else}
            {@const IconComp = ae_util.file_extension_icon_lucide(
                hosted_file_obj?.extension
            )}
            <div class="flex w-full items-center">
                <div
                    class="flex shrink-0 items-center pr-2 {show_divider
                        ? 'border-surface-500/30 mr-2 border-r'
                        : ''}">
                    <IconComp size={18} />
                </div>
                <span class="grow truncate text-left">
                    {shortened_filename}
                </span>
                {#if hosted_file_obj?.file_purpose || hosted_file_obj?.group}
                    <span
                        class="badge preset-tonal-success ml-2 shrink-0 text-[10px] font-bold uppercase">
                        {hosted_file_obj.file_purpose || hosted_file_obj.group}
                    </span>
                {/if}
            </div>
        {/if}
    {/await}

    {#if download_complete === null}
        <span class="ml-2 whitespace-nowrap text-red-800 dark:text-red-200"
            >File not found</span>
    {:else if download_complete === false}
        <span
            class="ml-2 text-xs whitespace-nowrap text-red-800 dark:text-red-200"
            >Failed!</span>
    {/if}
{/snippet}

{#if hosted_file_id && hosted_file_obj}
    {@const file_id = hosted_file_obj.hosted_file_id ?? hosted_file_id}

    {#if show_direct_download}
        <a
            href={direct_download_url}
            download={ae_util.clean_filename(final_filename)}
            class={variant_classes}
            title={`Direct download (V3 Action):\n${final_filename}\n[API] SHA256: ${hosted_file_obj?.hash_sha256?.slice(0, 10)}...\nHosted ID: ${file_id}`}>
            {@render content()}
        </a>
    {:else}
        <button
            type="button"
            disabled={require_auth && !$ae_loc.authenticated_access}
            class={variant_classes}
            onclick={handle_click}
            title={
`Download this file:
${final_filename}
[API] SHA256: ${hosted_file_obj?.hash_sha256?.slice(0, 10)}...
Hosted ID: ${file_id}

File size: ${hosted_file_obj.file_size ? ae_util.format_bytes(hosted_file_obj.file_size) : 'Unknown size'}
Created on: ${ae_util.iso_datetime_formatter(hosted_file_obj.created_on, 'datetime_short')}
Updated on: ${ae_util.iso_datetime_formatter(hosted_file_obj.updated_on, 'datetime_short')}

Open with: ${hosted_file_obj.open_in_os == 'win' ? 'Windows' : hosted_file_obj.open_in_os == 'mac' ? 'macOS' : hosted_file_obj.open_in_os == 'linux' ? 'Linux' : '--not set--'}

Linked to Type: ${linked_to_type ?? '--none--'} ID: ${linked_to_id ?? '---'}`
            }>
            {@render content()}
        </button>
    {/if}
{:else}
    <button
        type="button"
        disabled
        class={variant_classes}
        title="No file selected">
        <div class="flex w-full items-center">
            <div
                class="flex shrink-0 items-center pr-2 {show_divider
                    ? 'border-surface-500/30 mr-2 border-r'
                    : ''}">
                <Lucide.FileX size={18} />
            </div>
            <span class="grow text-left"> No file info </span>
        </div>
    </button>
{/if}
