<script lang="ts">
import { ae_api, ae_loc } from '$lib/stores/ae_stores';
import { events_func } from '$lib/ae_events/ae_events_functions';
import { launcher_loc } from '$lib/stores/ae_events_stores__launcher.svelte';
import {
    DEFAULT_LAUNCH_PROFILE_DEFS,
    resolve_launch_profile,
    type LaunchProfile
} from '$lib/ae_events/ae_launcher__default_launch_profiles';
import Launcher_Cfg_Section from './launcher_cfg_section.svelte';
import { AlarmClock, RotateCcw, Save, Timer } from '@lucide/svelte';

interface Props {
    on_expand?: () => void;
}

let { on_expand }: Props = $props();

type LaunchProfileOverride = Record<string, unknown> & {
    post_delay_ms?: number | string | null;
};

type LaunchProfilesBucket = Record<string, LaunchProfileOverride>;

type NativeDeviceLike = {
    event_device_id?: string;
    id?: string;
    other_json?: string | Record<string, unknown> | null;
    launch_profiles?: LaunchProfilesBucket | null;
    [key: string]: unknown;
};

type ProfileRow = {
    name: string;
    aliases: string[];
    profile: LaunchProfile;
};

const profile_rows: ProfileRow[] = DEFAULT_LAUNCH_PROFILE_DEFS.filter(
    ({ profile }) => profile.post_delay_ms !== undefined
).map(({ name, aliases, profile }) => ({
    name,
    aliases,
    profile
}));

const profile_row_map: Record<string, ProfileRow> = Object.fromEntries(
    profile_rows.map((row) => [row.name, row])
);

let delay_inputs = $state<Record<string, string>>({});
let row_status = $state<Record<string, string>>({});
let last_device_id: string | null = null;

function get_native_device(): NativeDeviceLike | null {
    const store_loc = $ae_loc as { native_device?: NativeDeviceLike };
    return store_loc.native_device ?? null;
}

function get_device_id(): string | null {
    const native_device = get_native_device();
    return native_device?.event_device_id ?? native_device?.id ?? null;
}

function parse_other_json(raw_json: unknown): Record<string, unknown> {
    if (!raw_json) return {};
    if (typeof raw_json === 'object') return { ...(raw_json as Record<string, unknown>) };
    if (typeof raw_json !== 'string') return {};
    try {
        const parsed = JSON.parse(raw_json);
        return parsed && typeof parsed === 'object' ? { ...parsed } : {};
    } catch {
        return {};
    }
}

function get_device_launch_profiles(): LaunchProfilesBucket {
    const native_device = get_native_device();
    const parsed_other_json = parse_other_json(native_device?.other_json);
    const launcher_cfg =
        (parsed_other_json.launcher as Record<string, unknown> | undefined) ?? {};

    return (
        (launcher_cfg.launch_profiles as LaunchProfilesBucket | undefined) ??
        (native_device?.launch_profiles as LaunchProfilesBucket | undefined) ??
        {}
    );
}

function get_effective_delay_ms(profile_name: string): number | null {
    const bucket = get_device_launch_profiles();
    const profile = resolve_launch_profile(
        profile_name,
        null,
        bucket as Record<string, Partial<LaunchProfile>>,
        null
    );
    const delay = profile.post_delay_ms;
    const parsed_delay =
        typeof delay === 'string'
            ? Number(delay)
            : typeof delay === 'number'
                ? delay
                : Number.NaN;

    return Number.isFinite(parsed_delay) && parsed_delay >= 0
        ? parsed_delay
        : null;
}

function sync_from_device() {
    const device_id = get_device_id();
    if (device_id === last_device_id) return;

    last_device_id = device_id;

    for (const row of profile_rows) {
        const current_delay = get_effective_delay_ms(row.name);
        delay_inputs[row.name] = current_delay === null ? '' : String(current_delay);
    }
}

sync_from_device();

$effect(() => {
    sync_from_device();
});

function set_row_status(profile_name: string, message: string) {
    row_status[profile_name] = message;
    setTimeout(() => {
        if (row_status[profile_name] === message) {
            row_status[profile_name] = '';
        }
    }, 3000);
}

function build_other_json(profile_name: string, delay_value: number | null): string {
    const native_device = get_native_device();
    const other_json = parse_other_json(native_device?.other_json);
    const launcher_cfg = {
        ...((other_json.launcher as Record<string, unknown> | undefined) ?? {})
    };
    const existing_profiles =
        (launcher_cfg.launch_profiles as LaunchProfilesBucket | undefined) ??
        (native_device?.launch_profiles as LaunchProfilesBucket | undefined) ??
        {};
    const next_profiles: LaunchProfilesBucket = { ...existing_profiles };
    const row = profile_row_map[profile_name];
    const alias_keys = row?.aliases ?? [];

    let source_key: string | null = null;
    if (profile_name in next_profiles) {
        source_key = profile_name;
    } else {
        for (const alias_key of alias_keys) {
            if (alias_key in next_profiles) {
                source_key = alias_key;
                break;
            }
        }
    }

    const source_profile = source_key
        ? { ...(next_profiles[source_key] as LaunchProfileOverride) }
        : {};

    if (source_key && source_key !== profile_name) {
        delete next_profiles[source_key];
    }

    for (const alias_key of alias_keys) {
        if (alias_key !== source_key) {
            delete next_profiles[alias_key];
        }
    }

    const default_delay_ms = row?.profile.post_delay_ms ?? null;
    if (delay_value === null || delay_value === default_delay_ms) {
        delete source_profile.post_delay_ms;
    } else {
        source_profile.post_delay_ms = delay_value;
    }

    if (Object.keys(source_profile).length > 0) {
        next_profiles[profile_name] = source_profile;
    } else {
        delete next_profiles[profile_name];
    }

    if (Object.keys(next_profiles).length > 0) {
        launcher_cfg.launch_profiles = next_profiles;
    } else {
        delete launcher_cfg.launch_profiles;
    }

    if (Object.keys(launcher_cfg).length > 0) {
        other_json.launcher = launcher_cfg;
    } else {
        delete other_json.launcher;
    }

    return JSON.stringify(other_json);
}

async function save_profile_delay(profile_name: string) {
    const device_id = get_device_id();
    if (!device_id) {
        set_row_status(profile_name, 'No native device loaded');
        return;
    }

    const trimmed = (delay_inputs[profile_name] ?? '').trim();
    const row = profile_row_map[profile_name];
    const default_delay_ms = row?.profile.post_delay_ms ?? null;
    let delay_value: number | null = null;

    if (trimmed !== '') {
        const parsed = Number(trimmed);
        if (!Number.isFinite(parsed) || parsed < 0) {
            set_row_status(profile_name, 'Enter a valid non-negative delay');
            return;
        }
        delay_value = Math.round(parsed);
    }

    set_row_status(profile_name, 'Saving...');
    const other_json = build_other_json(profile_name, delay_value);
    const result = await events_func.update_ae_obj__event_device({
        api_cfg: $ae_api,
        event_device_id: device_id,
        data_kv: {
            other_json
        },
        log_lvl: 0
    });

    if (!result) {
        set_row_status(profile_name, 'Save failed');
        return;
    }

    const store_loc = $ae_loc as { native_device?: NativeDeviceLike };
    store_loc.native_device = {
        ...get_native_device(),
        ...result,
        other_json
    };

    if (delay_value === null || delay_value === default_delay_ms) {
        delay_inputs[profile_name] = default_delay_ms === null ? '' : String(default_delay_ms);
        set_row_status(profile_name, 'Reset to built-in default');
    } else {
        delay_inputs[profile_name] = String(delay_value);
        set_row_status(profile_name, 'Saved');
    }
}

async function reset_profile_delay(profile_name: string) {
    const row = profile_row_map[profile_name];
    delay_inputs[profile_name] = row?.profile.post_delay_ms === null
        ? ''
        : String(row?.profile.post_delay_ms ?? '');
    await save_profile_delay(profile_name);
}
</script>

<Launcher_Cfg_Section
    title="Launch Timing"
    icon={Timer}
    bind:state={launcher_loc.current.section_state__launch_timing}
    {on_expand}
    description="Per-profile post-open delay overrides">
    {#if $ae_loc.edit_mode && !$ae_loc.is_native}
        <div
            class="bg-warning-500/10 border-warning-500/30 mb-1 flex items-center gap-2 rounded-lg border px-2 py-1.5">
            <AlarmClock size="0.75em" class="text-warning-500" />
            <span
                class="text-warning-500 text-[9px] font-bold tracking-wide uppercase"
                >Dev Preview — visible for layout only; save requires a native
                device record</span>
        </div>
    {/if}

    <div class="flex flex-col gap-2">
        <p class="ml-1 text-[9px] font-bold uppercase opacity-50">
            Per-Profile Native Open Delay
        </p>
        <p class="ml-1 text-[10px] leading-snug opacity-60">
            Each built-in profile keeps its own timing. Leave a value at the
            built-in default to avoid storing an override for that profile.
        </p>

        {#if !get_device_id()}
            <div
                class="bg-warning-500/10 border-warning-500/20 rounded-lg border px-2 py-2 text-[9px] opacity-80">
                No native device record is loaded yet, so these controls are
                read-only until the device config arrives.
            </div>
        {/if}

        <div class="flex flex-col gap-2">
            {#each profile_rows as row (row.name)}
                <section class="border-surface-500/10 rounded-lg border p-2">
                    <div class="mb-2 flex items-start justify-between gap-3">
                        <div class="flex flex-col gap-0.5">
                            <p class="text-[10px] font-bold uppercase tracking-wide">
                                {row.profile.app}
                            </p>
                            <p class="text-[9px] opacity-55">
                                {row.name}
                                {#if row.aliases.length}
                                    <span class="opacity-50"
                                        >• {row.aliases.join(', ')}</span
                                    >
                                {/if}
                            </p>
                        </div>

                        <div class="text-right text-[9px] min-w-22 leading-tight opacity-65">
                            <div>
                                Built-in:
                                {row.profile.post_delay_ms ?? 'n/a'} ms
                            </div>
                            <div>
                                Current:
                                {get_effective_delay_ms(row.name) ?? 'n/a'} ms
                            </div>
                        </div>
                    </div>

                    <div class="grid grid-cols-[1fr_auto_auto] gap-2">
                        <input
                            type="number"
                            min="0"
                            step="100"
                            bind:value={delay_inputs[row.name]}
                            placeholder={`Default: ${row.profile.post_delay_ms ?? 'n/a'} ms`}
                            class="input input-sm preset-tonal-surface h-8 text-[10px]"
                            disabled={!get_device_id()} />

                        <button
                            type="button"
                            onclick={() => save_profile_delay(row.name)}
                            class="btn btn-sm preset-filled-primary h-8 px-3 text-[10px] font-bold"
                            disabled={!get_device_id()}>
                            <Save size="0.8em" class="mr-1" /> Save
                        </button>

                        <button
                            type="button"
                            onclick={() => reset_profile_delay(row.name)}
                            class="btn btn-sm preset-tonal-surface h-8 px-3 text-[10px]"
                            disabled={!get_device_id()}>
                            <RotateCcw size="0.8em" class="mr-1" /> Reset
                        </button>
                    </div>

                    {#if row_status[row.name]}
                        <div class="text-primary-500 mt-1 text-[9px] italic">
                            {row_status[row.name]}
                        </div>
                    {/if}
                </section>
            {/each}
        </div>
    </div>
</Launcher_Cfg_Section>