/*
 * Copyright 2026 Element Creations Ltd.
 *
 * SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
 * Please see LICENSE files in the repository root for full details.
 */

import { type Room } from "matrix-js-sdk/src/matrix";
import {
    BaseViewModel,
    type RoomListSectionHeaderActions,
    type RoomListSectionHeaderViewSnapshot,
} from "@element-hq/web-shared-components";

import { RoomNotificationStateStore } from "../../stores/notifications/RoomNotificationStateStore";
import { NotificationStateEvents } from "../../stores/notifications/NotificationState";
import { type RoomNotificationState } from "../../stores/notifications/RoomNotificationState";
import SettingsStore from "../../settings/SettingsStore";
import { DefaultTagID } from "../../stores/room-list-v3/skip-list/tag";
import RoomListStoreV3, { CHATS_TAG } from "../../stores/room-list-v3/RoomListStoreV3";

interface RoomListSectionHeaderViewModelProps {
    tag: string;
    title: string;
    /**
     * The ID of the current space.
     */
    spaceId: string;
    onToggleExpanded: (isExpanded: boolean) => void;
}

export class RoomListSectionHeaderViewModel
    extends BaseViewModel<RoomListSectionHeaderViewSnapshot, RoomListSectionHeaderViewModelProps>
    implements RoomListSectionHeaderActions
{
    /**
     * The notification states of the rooms currently in this section, used to compute the unread state.
     */
    private roomNotificationStates = new Set<RoomNotificationState>();

    /**
     * Tracks the expanded/collapsed state per space.
     * Key is spaceId. Defaults to expanded if not set.
     */
    private readonly expandedBySpace = new Map<string, boolean>();

    public constructor(props: RoomListSectionHeaderViewModelProps) {
        const isDefaultSection =
            props.tag === DefaultTagID.Favourite || props.tag === DefaultTagID.LowPriority || props.tag === CHATS_TAG;
        super(props, {
            id: props.tag,
            title: props.title,
            isExpanded: true,
            isUnread: false,
            displaySectionMenu: !isDefaultSection,
        });
        const sectionWatherRef = SettingsStore.watchSetting("RoomList.CustomSectionData", null, () =>
            this.onCustomSectionDataChange(),
        );
        this.disposables.track(() => SettingsStore.unwatchSetting(sectionWatherRef));
    }

    public onClick = (): void => {
        const isExpanded = !this.snapshot.current.isExpanded;
        this.expandedBySpace.set(this.props.spaceId, isExpanded);
        this.snapshot.merge({ isExpanded });
        this.props.onToggleExpanded(isExpanded);
    };

    /**
     * Whether the section is currently expanded or not.
     */
    public get isExpanded(): boolean {
        return this.snapshot.current.isExpanded;
    }

    /**
     * Set whether the section is expanded for the current space.
     * This will not trigger the onToggleExpanded callback.
     */
    public set isExpanded(value: boolean) {
        this.expandedBySpace.set(this.props.spaceId, value);
        this.snapshot.merge({ isExpanded: value });
    }

    /**
     * Switch to a different space, restoring the expanded state for that space.
     * Defaults to expanded if no state has been saved for the space.
     */
    public setSpace(spaceId: string): void {
        this.props.spaceId = spaceId;
        const isExpanded = this.expandedBySpace.get(this.props.spaceId) ?? true;
        this.snapshot.merge({ isExpanded });
    }

    /**
     * Update the rooms tracked by this section header for unread state computation.
     * Only subscribes to new rooms and unsubscribes from rooms no longer in the section.
     * @param rooms - The rooms currently in this section
     */
    public setRooms(rooms: Room[]): void {
        const newStates = new Set(rooms.map((room) => RoomNotificationStateStore.instance.getRoomState(room)));

        // Unsubscribe from rooms no longer in the section
        for (const state of this.roomNotificationStates) {
            if (!newStates.has(state)) {
                state.off(NotificationStateEvents.Update, this.updateUnreadState);
            }
        }

        // Subscribe to newly added rooms
        for (const state of newStates) {
            if (!this.roomNotificationStates.has(state)) {
                // We don't use trackListener because we don't want to grow the disposables indefinitely as rooms are added and removed from the section
                state.on(NotificationStateEvents.Update, this.updateUnreadState);
            }
        }

        this.roomNotificationStates = newStates;
        this.updateUnreadState();
    }

    /**
     * Update the unread state of the section header based on the notification states of the tracked rooms.
     */
    private updateUnreadState = (): void => {
        const isUnread = [...this.roomNotificationStates].some((state) => state.hasAnyNotificationOrActivity);
        this.snapshot.merge({ isUnread });
    };

    public dispose(): void {
        for (const state of this.roomNotificationStates) {
            state.off(NotificationStateEvents.Update, this.updateUnreadState);
        }
        this.roomNotificationStates.clear();
        super.dispose();
    }

    /**
     * Handle changes to custom section data.
     */
    private onCustomSectionDataChange(): void {
        const customSectionData = SettingsStore.getValue("RoomList.CustomSectionData") || {};
        const sectionData = customSectionData[this.props.tag];
        if (sectionData) {
            this.snapshot.merge({ title: sectionData.name });
        }
    }

    public editSection = async (): Promise<void> => {
        await RoomListStoreV3.instance.editSection(this.props.tag);
    };

    public removeSection = async (): Promise<void> => {
        // There is one notification state per room in the section
        const isEmpty = this.roomNotificationStates.size === 0;
        await RoomListStoreV3.instance.removeSection(this.props.tag, isEmpty);
    };
}
