<template>
    <div class="mobile-session-swipe">
        <div v-if="canSwipe" class="swipe-actions">
            <button v-if="swipeActions.includes('mark-unread')" class="action-btn action-unread" type="button"
                @click.stop="handleAction('mark-unread')">
                {{ t('标记未读') }}
            </button>
            <button v-if="swipeActions.includes('toggle-mark')" class="action-btn action-mark" type="button"
                @click.stop="handleAction('toggle-mark')">
                {{ session.marked ? t('取消标记') : t('标记') }}
            </button>
            <button v-if="swipeActions.includes('delete')" class="action-btn action-delete" type="button"
                @click.stop="handleAction('delete')">
                {{ t('删除') }}
            </button>
        </div>

        <div class="mobile-session-item"
            :class="{ 'is-open': isSwipeOpen, dragging: isDragging, 'is-ringing': isRinging, 'is-disabled': isDisabled }"
            :style="itemStyle" @click="handleItemClick" @touchstart="handleTouchStart" @touchmove="handleTouchMove"
            @touchend="handleTouchEnd" @touchcancel="handleTouchEnd">
            <div class="avatar-wrapper">
                <div class="avatar-shell" :class="{ 'is-ringing': isRinging }" :style="ringingAvatarStyle">
                    <div class="avatar">
                        <img class="avatar-image" :src="session.avatar || defaultAvatar" alt="" />
                    </div>
                </div>
                <span class="badge" v-if="session.unread">{{ session.unread > 99 ? '99+' : session.unread }}</span>
            </div>
            <div class="info" :class="{ 'is-ringing': isRinging }">
                <div class="top-row">
                    <div class="name-row">
                        <SessionSeatStatus v-if="session.type === 'im_other_inprogress' && session.seatStatus"
                            :seat-status="session.seatStatus" :rest-reason="session.restReason" />
                        <span class="name">{{ session.name }}</span>
                    </div>
                    <div class="meta-info" :class="{ 'is-ringing': isRinging, 'status-meta': showSessionStatus }">
                        <button v-if="isRinging" class="accept-btn" type="button" @click.stop="handleAccept">
                            {{ t('介入（{seconds}）', { seconds: displayTimeout }) }}
                        </button>
                        <template v-else-if="showSessionStatus">
                            <img class="status-icon" :src="correctIcon" alt="" />
                            <span class="status-text">{{ session.overlayText }}</span>
                        </template>
                        <template v-else>
                            <img v-if="session.marked" class="mark-icon" :src="sessionMarkedIcon" alt="" />
                            <SessionReplyTimeout v-if="!isFinishedIMSession && session.IMReplyTimeOut > 0"
                                :initial-seconds="session.IMReplyTimeOut" />
                            <span class="time" v-else-if="session.time">{{ session.time }}</span>
                        </template>
                    </div>
                </div>
                <div class="bottom-row">
                    <span class="msg"
                        :class="{ 'msg-placeholder': isFinishedIMSession || !session.lastMsg }">
                        {{ isFinishedIMSession ? t('占位') : (session.lastMsg || t('占位')) }}
                    </span>
                    <span v-if="showSessionStatus && session.statusHint" class="status-hint">{{ session.statusHint
                    }}</span>
                    <span class="source" v-else-if="!isRinging && session.source">{{ session.source }}</span>
                </div>
            </div>
        </div>
    </div>
</template>

<script setup lang="ts">
import { computed, onUnmounted, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import type { Session, SessionSwipeAction } from './index.vue';
import sessionMarkedIcon from './sessionMarked.svg';
import defaultAvatar from '../../assets/icons/defaultAvatar.svg';
import correctIcon from '../../assets/icons/correct.svg';
import SessionReplyTimeout from './SessionReplyTimeout.vue';
import SessionSeatStatus from './SessionSeatStatus.vue';

const ACTION_WIDTH = 80;
const RINGING_TIMER_INTERVAL = 100;

const props = defineProps<{
    session: Session;
    isSwipeOpen?: boolean;
}>();

const emit = defineEmits<{
    (e: 'click'): void;
    (e: 'accept'): void;
    (e: 'swipe-open'): void;
    (e: 'swipe-close'): void;
    (e: 'mark-unread'): void;
    (e: 'toggle-mark'): void;
    (e: 'delete'): void;
}>();

const { t } = useI18n();

const translateX = ref(0);
const isDragging = ref(false);
const startX = ref(0);
const startY = ref(0);
const startTranslateX = ref(0);
const isHorizontalSwipe = ref(false);
const suppressClick = ref(false);
const now = ref(Date.now());
const countdownStartAt = ref(Date.now());
const countdownDuration = ref(0);
let ringingTimer: number | undefined;

const swipeActions = computed<SessionSwipeAction[]>(() => props.session.swipeActions ?? []);
const canSwipe = computed(() => swipeActions.value.length > 0);
const isDisabled = computed(() => Boolean(props.session.overlayText));
const showSessionStatus = computed(() => Boolean(props.session.overlayText));
const isFinishedIMSession = computed(
    () => props.session.type === 'im' && props.session.sessionState === 'finished',
);
const maxSwipeDistance = computed(() => ACTION_WIDTH * swipeActions.value.length);
const openThreshold = computed(() => Math.min(ACTION_WIDTH, maxSwipeDistance.value / 2));
const isRinging = computed(() => props.session.sessionState === 'ringing');
const timeoutValue = computed(() => {
    const nextTimeout = Number(props.session.timeout ?? 0);
    return Number.isFinite(nextTimeout) ? Math.max(nextTimeout, 0) : 0;
});
const remainingTimeout = computed(() => {
    if (!isRinging.value) {
        return timeoutValue.value;
    }

    if (countdownDuration.value <= 0) {
        return 0;
    }

    const elapsedSeconds = Math.max((now.value - countdownStartAt.value) / 1000, 0);
    return Math.max(countdownDuration.value - elapsedSeconds, 0);
});
const displayTimeout = computed(() => Math.max(0, Math.ceil(remainingTimeout.value)));
const ringProgress = computed(() => {
    if (countdownDuration.value <= 0) {
        return 0;
    }

    return Math.min(1, Math.max(remainingTimeout.value / countdownDuration.value, 0));
});
const ringingAvatarStyle = computed(() => {
    if (!isRinging.value) {
        return undefined;
    }

    const elapsedAngle = (1 - ringProgress.value) * 360;
    return {
        background: `conic-gradient(from 0deg, #E4F7EE 0deg ${elapsedAngle}deg, #0ABF77 ${elapsedAngle}deg 360deg)`,
    };
});

watch(
    [() => props.isSwipeOpen, swipeActions],
    ([isOpen]) => {
        translateX.value = isOpen && canSwipe.value ? -maxSwipeDistance.value : 0;
        if (!isOpen) {
            isDragging.value = false;
            isHorizontalSwipe.value = false;
        }
    },
    { immediate: true },
);

const stopRingingTimer = () => {
    if (ringingTimer !== undefined) {
        window.clearInterval(ringingTimer);
        ringingTimer = undefined;
    }
};

const startRingingTimer = () => {
    stopRingingTimer();

    const nextTimeout = timeoutValue.value;
    countdownDuration.value = nextTimeout;
    countdownStartAt.value = Date.now();
    now.value = countdownStartAt.value;

    if (!isRinging.value || nextTimeout <= 0) {
        return;
    }

    ringingTimer = window.setInterval(() => {
        now.value = Date.now();
        if (remainingTimeout.value <= 0) {
            stopRingingTimer();
        }
    }, RINGING_TIMER_INTERVAL);
};

watch(
    [isRinging, () => props.session.timeout],
    () => {
        startRingingTimer();
    },
    { immediate: true },
);

onUnmounted(() => {
    stopRingingTimer();
});

const itemStyle = computed(() => ({
    transform: `translate3d(${translateX.value}px, 0, 0)`,
    transition: isDragging.value ? 'none' : 'transform 0.2s ease',
}));

const closeSwipe = () => {
    isDragging.value = false;
    isHorizontalSwipe.value = false;
    translateX.value = 0;
    emit('swipe-close');
};

const openSwipe = () => {
    if (!canSwipe.value) {
        return;
    }

    isDragging.value = false;
    isHorizontalSwipe.value = false;
    translateX.value = -maxSwipeDistance.value;
    emit('swipe-open');
};

const handleTouchStart = (event: TouchEvent) => {
    if (isDisabled.value || !canSwipe.value) {
        return;
    }

    const touch = event.touches[0];
    if (!touch) {
        return;
    }

    startX.value = touch.clientX;
    startY.value = touch.clientY;
    startTranslateX.value = translateX.value;
    isDragging.value = true;
    isHorizontalSwipe.value = false;
    suppressClick.value = false;
};

const handleTouchMove = (event: TouchEvent) => {
    const touch = event.touches[0];
    if (!touch || !isDragging.value || !canSwipe.value) {
        return;
    }

    const deltaX = touch.clientX - startX.value;
    const deltaY = touch.clientY - startY.value;

    if (!isHorizontalSwipe.value) {
        if (Math.abs(deltaX) < 8) {
            return;
        }

        if (Math.abs(deltaX) <= Math.abs(deltaY)) {
            isDragging.value = false;
            return;
        }

        isHorizontalSwipe.value = true;
    }

    event.preventDefault();
    suppressClick.value = true;
    translateX.value = Math.min(0, Math.max(-maxSwipeDistance.value, startTranslateX.value + deltaX));
};

const handleTouchEnd = () => {
    if (!isDragging.value && !isHorizontalSwipe.value) {
        return;
    }

    const shouldOpen = Math.abs(translateX.value) > openThreshold.value;

    if (shouldOpen) {
        openSwipe();
        return;
    }

    closeSwipe();
};

const handleItemClick = () => {
    if (suppressClick.value) {
        suppressClick.value = false;
        return;
    }

    if (isDisabled.value) {
        return;
    }

    if (props.isSwipeOpen && canSwipe.value) {
        closeSwipe();
        return;
    }

    emit('click');
};

const handleAccept = () => {
    emit('accept');
};

const handleAction = (action: SessionSwipeAction) => {
    if (action === 'mark-unread') {
        emit('mark-unread');
        return;
    }

    if (action === 'toggle-mark') {
        emit('toggle-mark');
        return;
    }

    emit('delete');
};
</script>

<style scoped>
.mobile-session-swipe {
    position: relative;
    overflow: hidden;
    background: #fff;
}

.swipe-actions {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    display: flex;
    align-items: stretch;
    justify-content: flex-end;
}

.action-btn {
    width: 80px;
    border: none;
    color: #fff;
    font-size: 12px;
    font-weight: 500;
    padding: 0 8px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.action-unread {
    background: #1677ff;
}

.action-mark {
    background: #fa8c16;
}

.action-delete {
    background: #ff4d4f;
}

.mobile-session-item {
    position: relative;
    z-index: 1;
    display: flex;
    padding: 16px;
    cursor: pointer;
    border-bottom: 1px solid #f0f0f0;
    background-color: #fff;
    touch-action: pan-y;
}

.mobile-session-item:active {
    background-color: #f5f5f5;
}

.mobile-session-item.is-disabled {
    cursor: default;
}

.mobile-session-item.is-disabled:active {
    background-color: #fff;
}

.avatar-wrapper {
    position: relative;
    margin-right: 16px;
    flex-shrink: 0;
}

.avatar-shell {
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
}

.avatar-shell.is-ringing {
    padding: 3px;
}

.avatar {
    width: 48px;
    height: 48px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 18px;
    overflow: hidden;
}

.avatar-image {
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: inherit;
    display: block;
}

.badge {
    position: absolute;
    top: -4px;
    right: -4px;
    background: #ff4d4f;
    color: #fff;
    font-size: 10px;
    padding: 0 4px;
    border-radius: 10px;
    line-height: 16px;
    height: 16px;
    min-width: 16px;
    text-align: center;
    border: 1px solid #fff;
}

.info {
    position: relative;
    flex: 1;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    justify-content: center;
}

.info.is-ringing {
    padding-right: 96px;
}

.top-row,
.bottom-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 8px;
}

.top-row {
    margin-bottom: 6px;
}

.name-row {
    display: flex;
    align-items: center;
    gap: 6px;
    flex: 1;
    min-width: 0;
}

.name {
    font-size: 16px;
    color: #333;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    flex: 1;
    min-width: 0;
}

.meta-info {
    display: flex;
    align-items: center;
    flex-shrink: 0;
    gap: 6px;
    margin-left: 8px;
}

.meta-info.status-meta {
    color: #0ABF77;
    min-width: 0;
    max-width: 60%;
}

.meta-info.is-ringing {
    position: absolute;
    top: 50%;
    right: 0;
    transform: translateY(-50%);
    margin-left: 0;
    z-index: 1;
}

.status-icon {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
    filter: invert(56%) sepia(73%) saturate(542%) hue-rotate(106deg) brightness(92%) contrast(93%);
}

.status-text {
    font-size: 12px;
    line-height: 18px;
    color: #0ABF77;
    white-space: nowrap;
}

.accept-btn {
    min-width: 76px;
    height: 30px;
    padding: 0 12px;
    border: none;
    border-radius: 15px;
    background: #0ABF77;
    color: #fff;
    font-size: 12px;
    font-weight: 500;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.mark-icon {
    width: 12px;
    height: 12px;
    flex-shrink: 0;
}

.time {
    font-size: 12px;
    color: #999;
    flex-shrink: 0;
}

.msg {
    font-size: 14px;
    color: #999;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    flex: 1;
    min-width: 0;
}

.msg-placeholder {
    visibility: hidden;
}

.status-hint {
    font-size: 12px;
    line-height: 18px;
    color: #999;
    flex-shrink: 0;
    white-space: nowrap;
}

.source {
    font-size: 11px;
    color: #999;
    padding: 2px 0px;
    border-radius: 3px;
    flex-shrink: 0;
    margin-left: 8px;
}

.session-overlay {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(255, 255, 255, 0.76);
    z-index: 2;
    pointer-events: none;
}

.session-overlay-tag {
    display: inline-flex;
    align-items: center;
    max-width: calc(100% - 32px);
    padding: 6px 12px;
    border-radius: 999px;
    background: rgba(0, 0, 0, 0.68);
    color: #fff;
    font-size: 12px;
    line-height: 1;
    white-space: nowrap;
}

.session-overlay-icon {
    position: relative;
    width: 14px;
    height: 14px;
    margin-right: 6px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.2);
    flex-shrink: 0;
}

.session-overlay-icon::before {
    content: '';
    position: absolute;
    top: 3px;
    left: 3px;
    width: 5px;
    height: 5px;
    border-top: 2px solid #fff;
    border-right: 2px solid #fff;
    transform: rotate(45deg);
}
</style>
