<script lang="ts">
    import { browser } from '$app/environment';
    import { goto } from '$app/navigation';
    import { changePassword, logout } from '$lib/api/auth';
    import { ApiError } from '$lib/api/client';
    import { preferences } from '$lib/preferences.svelte';
    import { session } from '$lib/session.svelte';
    import Sheet from '$lib/components/Sheet.svelte';
    import User from '@lucide/svelte/icons/user';
    import SlidersHorizontal from '@lucide/svelte/icons/sliders-horizontal';
    import KeyRound from '@lucide/svelte/icons/key-round';
    import LogOut from '@lucide/svelte/icons/log-out';
    import Shield from '@lucide/svelte/icons/shield';
    import ChevronRight from '@lucide/svelte/icons/chevron-right';

    let currentPassword = $state('');
    let newPassword = $state('');
    let confirmPassword = $state('');
    let submitting = $state(false);
    let success = $state<string | null>(null);
    let error: string | null = $state(null);

    let isMobileViewport = $state(false);
    let passwordSheetOpen = $state(false);
    let loggingOut = $state(false);

    const passwordsMatch = $derived(
        newPassword.length > 0 && newPassword === confirmPassword
    );
    const canSubmit = $derived(
        Boolean(session.user) &&
            currentPassword.length > 0 &&
            newPassword.length >= 8 &&
            passwordsMatch &&
            !submitting
    );

    const memberSince = $derived(
        session.user ? new Date(session.user.created_at).toLocaleDateString() : null
    );

    $effect(() => {
        if (!browser) return;
        const mql = window.matchMedia('(max-width: 640px)');
        isMobileViewport = mql.matches;
        const onChange = (e: MediaQueryListEvent) => {
            isMobileViewport = e.matches;
        };
        mql.addEventListener('change', onChange);
        return () => mql.removeEventListener('change', onChange);
    });

    async function submit(e: SubmitEvent) {
        e.preventDefault();
        if (!canSubmit) return;
        submitting = true;
        success = null;
        error = null;
        try {
            await changePassword({
                current_password: currentPassword,
                new_password: newPassword
            });
            success = 'Password updated. Other devices have been signed out.';
            currentPassword = '';
            newPassword = '';
            confirmPassword = '';
        } catch (e) {
            if (e instanceof ApiError && e.status === 401 && !session.user) {
                await goto('/login');
                return;
            }
            error = e instanceof Error ? e.message : String(e);
        } finally {
            submitting = false;
        }
    }

    // Mirror the root layout's logout flow so the user lands at /login
    // with the same session-clearing semantics. Replicated here rather
    // than imported because the layout's handler closes over its own
    // local `loggingOut` state.
    async function handleLogout() {
        loggingOut = true;
        try {
            await logout();
        } finally {
            session.setUser(null);
            preferences.clearForLogout();
            loggingOut = false;
            await goto('/login');
        }
    }
</script>

{#snippet passwordForm()}
    <form onsubmit={submit} action="javascript:void(0)" data-testid="password-form">
        <label class="form-field">
            <span>Current password</span>
            <input
                type="password"
                bind:value={currentPassword}
                autocomplete="current-password"
                required
                data-testid="current-password"
            />
        </label>
        <label class="form-field">
            <span>New password</span>
            <input
                type="password"
                bind:value={newPassword}
                autocomplete="new-password"
                minlength="8"
                required
                data-testid="new-password"
            />
        </label>
        <label class="form-field">
            <span>Confirm new password</span>
            <input
                type="password"
                bind:value={confirmPassword}
                autocomplete="new-password"
                minlength="8"
                required
                data-testid="confirm-password"
            />
            {#if confirmPassword.length > 0 && !passwordsMatch}
                <span class="field-error" role="alert" data-testid="mismatch">
                    Passwords don't match.
                </span>
            {/if}
        </label>
        <button
            class="primary"
            type="submit"
            disabled={!canSubmit}
            data-testid="password-submit"
        >
            {submitting ? 'Updating…' : 'Update password'}
        </button>
        {#if success}
            <p class="success" data-testid="password-success">{success}</p>
        {/if}
        {#if error}
            <p role="alert" class="form-error" data-testid="password-error">{error}</p>
        {/if}
    </form>
{/snippet}

{#if !session.user}
    <p class="status" data-testid="account-signin">
        <a href="/login?next=/profile/account">Sign in</a> to manage your account.
    </p>
{:else if isMobileViewport}
    <div class="mobile-hub" data-testid="account-hub">
        <header class="hub-profile" data-testid="account-hub-profile">
            <div class="hub-avatar" aria-hidden="true">
                <User size={28} aria-hidden="true" />
            </div>
            <div class="hub-name" data-testid="account-username">
                {session.user.username}
            </div>
            {#if memberSince}
                <div class="hub-meta">Member since {memberSince}</div>
            {/if}
        </header>

        <div class="row-group">
            <a class="row" href="/profile" data-testid="account-row-profile">
                <User size={18} aria-hidden="true" />
                <span class="row-label">Profile</span>
                <ChevronRight size={16} aria-hidden="true" class="row-chev" />
            </a>
            <a
                class="row"
                href="/profile/preferences"
                data-testid="account-row-preferences"
            >
                <SlidersHorizontal size={18} aria-hidden="true" />
                <span class="row-label">Preferences</span>
                <ChevronRight size={16} aria-hidden="true" class="row-chev" />
            </a>
            <button
                type="button"
                class="row"
                onclick={() => (passwordSheetOpen = true)}
                data-testid="account-row-change-password"
            >
                <KeyRound size={18} aria-hidden="true" />
                <span class="row-label">Change password</span>
                <ChevronRight size={16} aria-hidden="true" class="row-chev" />
            </button>
        </div>

        {#if session.user.is_admin}
            <!-- Admin is reachable from the desktop header; the mobile chrome
                 has no Admin tab, so the Account hub is its entry point. -->
            <div class="row-group">
                <a class="row" href="/admin" data-testid="account-row-admin">
                    <Shield size={18} aria-hidden="true" />
                    <span class="row-label">Admin</span>
                    <ChevronRight size={16} aria-hidden="true" class="row-chev" />
                </a>
            </div>
        {/if}

        <div class="row-group">
            <button
                type="button"
                class="row row-destructive"
                onclick={handleLogout}
                disabled={loggingOut}
                data-testid="account-row-logout"
            >
                <LogOut size={18} aria-hidden="true" />
                <span class="row-label">{loggingOut ? 'Logging out…' : 'Log out'}</span>
            </button>
        </div>
    </div>
{:else}
    <section class="card" data-testid="account-desktop-card">
        <h2>Change password</h2>
        <p class="hint">
            Changing your password signs out every other device using this account.
            Bot API tokens keep working — revoke them individually from the bot-token
            list if you want to invalidate them too.
        </p>
        {@render passwordForm()}
    </section>
{/if}

<Sheet
    open={passwordSheetOpen && isMobileViewport}
    title="Change password"
    onClose={() => (passwordSheetOpen = false)}
    testid="password-sheet"
>
    <p class="hint sheet-hint">
        Changing your password signs out every other device. Bot API tokens are
        unaffected.
    </p>
    {@render passwordForm()}
</Sheet>

<style>
    .card {
        background: var(--surface);
        border: 1px solid var(--border);
        border-radius: var(--radius-md);
        padding: var(--space-4);
        max-width: 32rem;
    }

    .hint {
        color: var(--text-muted);
        font-size: var(--font-sm);
    }

    .sheet-hint {
        margin-top: 0;
    }

    form {
        display: flex;
        flex-direction: column;
        gap: var(--space-3);
    }

    .primary {
        background: var(--primary);
        color: var(--primary-contrast);
        border-color: var(--primary);
        margin-top: var(--space-1);
        align-self: flex-start;
    }

    .primary:hover:not(:disabled) {
        background: var(--primary-hover);
        border-color: var(--primary-hover);
    }

    .field-error {
        color: var(--danger);
        font-size: var(--font-sm);
    }

    .form-error {
        color: var(--danger);
    }

    .success {
        color: var(--success);
    }

    .status {
        color: var(--text-muted);
    }

    /* ===== Mobile account hub (inset-grouped iOS-style list) ===== */

    .mobile-hub {
        display: flex;
        flex-direction: column;
        gap: var(--space-4);
        padding: var(--space-2) 0;
    }

    .hub-profile {
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: var(--space-1);
        padding: var(--space-4) 0 var(--space-2);
    }

    .hub-avatar {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 64px;
        height: 64px;
        border-radius: 50%;
        background: var(--surface-elevated);
        color: var(--text-muted);
        border: 1px solid var(--border);
        margin-bottom: var(--space-2);
    }

    .hub-name {
        font-size: var(--font-lg);
        font-weight: var(--weight-semibold);
        color: var(--text);
    }

    .hub-meta {
        color: var(--text-muted);
        font-size: var(--font-sm);
    }

    .row-group {
        background: var(--surface);
        border: 1px solid var(--border);
        border-radius: var(--radius-lg);
        overflow: hidden;
    }

    .row {
        display: flex;
        align-items: center;
        gap: var(--space-3);
        width: 100%;
        padding: var(--space-3) var(--space-4);
        background: transparent;
        border: 0;
        border-bottom: 1px solid var(--border);
        color: var(--text);
        text-decoration: none;
        text-align: left;
        font-size: var(--font-base);
        min-height: 52px;
        cursor: pointer;
    }

    .row:last-child {
        border-bottom: 0;
    }

    .row:hover:not(:disabled) {
        background: var(--surface-elevated);
        text-decoration: none;
    }

    .row-label {
        flex: 1;
        min-width: 0;
    }

    /* Lucide icons render as <svg>; need :global to penetrate scoping
       since we use `class={...}` on the icon, which is hashed by Svelte
       only when the selector matches a local element. */
    .row :global(.row-chev) {
        color: var(--text-muted);
        flex-shrink: 0;
    }

    .row-destructive {
        color: var(--danger);
        font-weight: var(--weight-medium);
        justify-content: center;
    }

    .row-destructive:hover:not(:disabled) {
        background: var(--danger-soft-bg);
    }

    .row:disabled {
        opacity: 0.6;
        cursor: not-allowed;
    }
</style>
