import { useCallback } from 'react';
import { GetRoomSession, HousekeepingApi, HousekeepingErrorKey, IHousekeepingActionResult, LocalizeText, NotificationBubbleType, validateAmount, validateBanHours, validatePositiveId, validateRank, validateReason } from '../../api';
import { useNotification } from '../notification';
import { useHousekeepingStore } from './useHousekeepingStore';

const SUCCESS_KEY = 'housekeeping.action.success';
const ERROR_KEY = 'housekeeping.action.error';

type ToastFn = (message: string, type: string, imageUrl?: string, internalLink?: string, senderName?: string) => void;

const localizeOrPassthrough = (key: string): string =>
{
    if(!key) return '';
    if(!key.includes('.')) return key;

    const localized = LocalizeText(key);

    return (localized === key) ? key : localized;
};

const wrap = async (
    runner: () => Promise<IHousekeepingActionResult>,
    markPending: () => void,
    markDone: (errorKey: string | null, successKey: string | null) => void,
    toast: ToastFn,
    recordMetric: (action: string, latencyMs: number, isError: boolean) => void,
    actionLabel: string
): Promise<IHousekeepingActionResult | null> =>
{
    markPending();

    const startedAt = typeof performance !== 'undefined' ? performance.now() : Date.now();
    const measure = (isError: boolean) =>
    {
        const endedAt = typeof performance !== 'undefined' ? performance.now() : Date.now();

        recordMetric(actionLabel, endedAt - startedAt, isError);
    };

    try
    {
        const result = await runner();

        if(result && result.ok === false)
        {
            // Error path: status banner only — the banner is inline
            // and stays put until dismissed, more visible than a
            // transient bubble for a failure that needs operator
            // attention.
            markDone(result.message || ERROR_KEY, null);
            measure(true);

            return result;
        }

        const successKey = result?.message || SUCCESS_KEY;

        markDone(null, successKey);
        // Success path also fires a transient toast so the operator
        // gets feedback without scanning the banner — banner stays
        // as a fallback for users that have bubbles disabled.
        toast(localizeOrPassthrough(successKey), NotificationBubbleType.INFO);
        measure(false);

        return result;
    }
    catch(error)
    {
        markDone(String((error as Error)?.message ?? error), null);
        measure(true);

        return null;
    }
};

const validationOr = (key: HousekeepingErrorKey, markDone: (e: string | null, s: string | null) => void): boolean =>
{
    if(key === HousekeepingErrorKey.NONE) return true;

    markDone(`housekeeping.validation.${ key }`, null);

    return false;
};

/**
 * Imperative facade for every HK admin action. State (selected
 * user/room, status banner) lives in `useHousekeepingStore`; this
 * hook reads it for context (e.g. the currently-selected target)
 * and writes only the action-pending / status flags via
 * `markActionPending` / `markActionDone`. Keeping the read-only
 * state in a separate filter would still work, but the singleton
 * store keeps invocation simple for the panel views that already
 * pull state via `useHousekeepingStore`.
 */
export const useHousekeepingActions = () =>
{
    const { selectedUser, selectedRoom, markActionPending, markActionDone, setSelectedUser, setSelectedRoom, recordActionMetric, revealPassword } = useHousekeepingStore();
    const { showSingleBubble } = useNotification();
    // Stable closure-bound runner so every action below stays a
    // one-liner: only the runner thunk + a per-action telemetry
    // label change per call site. The label keys into the metrics
    // map; a missing label defaults to "anonymous" so untagged calls
    // still produce a metric row.
    const runAction = useCallback((runner: () => Promise<IHousekeepingActionResult>, actionLabel: string = 'anonymous') =>
        wrap(runner, markActionPending, markActionDone, showSingleBubble, recordActionMetric, actionLabel),
    [ markActionPending, markActionDone, showSingleBubble, recordActionMetric ]);

    // -- USER --------------------------------------------------------
    const banUser = useCallback(async (userId: number, reason: string, hours: number) =>
    {
        if(!validationOr(validatePositiveId(userId, 'user'), markActionDone)) return null;
        if(!validationOr(validateReason(reason), markActionDone)) return null;
        if(!validationOr(validateBanHours(hours), markActionDone)) return null;

        return runAction(() => HousekeepingApi.banUser(userId, reason, hours), 'banUser');
    }, [ runAction, markActionDone ]);

    const unbanUser = useCallback(async (userId: number) =>
    {
        if(!validationOr(validatePositiveId(userId, 'user'), markActionDone)) return null;

        return runAction(() => HousekeepingApi.unbanUser(userId), 'unbanUser');
    }, [ runAction, markActionDone ]);

    const muteUser = useCallback(async (userId: number, reason: string, minutes: number) =>
    {
        if(!validationOr(validatePositiveId(userId, 'user'), markActionDone)) return null;
        if(!validationOr(validateReason(reason), markActionDone)) return null;
        if(!validationOr(validateBanHours(minutes), markActionDone)) return null;

        return runAction(() => HousekeepingApi.muteUser(userId, reason, minutes), 'muteUser');
    }, [ runAction, markActionDone ]);

    const kickUser = useCallback(async (userId: number, reason: string) =>
    {
        if(!validationOr(validatePositiveId(userId, 'user'), markActionDone)) return null;
        if(!validationOr(validateReason(reason), markActionDone)) return null;

        return runAction(() => HousekeepingApi.kickUser(userId, reason), 'kickUser');
    }, [ runAction, markActionDone ]);

    const forceDisconnectUser = useCallback(async (userId: number, reason: string) =>
    {
        if(!validationOr(validatePositiveId(userId, 'user'), markActionDone)) return null;
        if(!validationOr(validateReason(reason), markActionDone)) return null;

        return runAction(() => HousekeepingApi.forceDisconnectUser(userId, reason), 'forceDisconnectUser');
    }, [ runAction, markActionDone ]);

    const resetUserPassword = useCallback(async (userId: number) =>
    {
        if(!validationOr(validatePositiveId(userId, 'user'), markActionDone)) return null;

        // Run the action with a localizable success message — we
        // INTERCEPT before `wrap`'s default behavior leaks the plaintext
        // into the auto-dismissing status banner. The emulator returns
        // the freshly-generated plaintext in `result.message`; we lift it
        // into the dedicated `passwordReveal` slot which renders a
        // persistent card with a copy button. The wrapping `runAction`
        // would also fire a transient toast with whatever string lands
        // in `message`, so we bypass it via a direct API call + manual
        // status writes here.
        const username = (selectedUser && selectedUser.id === userId) ? selectedUser.username : '';

        markActionPending();
        const startedAt = typeof performance !== 'undefined' ? performance.now() : Date.now();
        const measure = (isError: boolean) =>
        {
            const endedAt = typeof performance !== 'undefined' ? performance.now() : Date.now();
            recordActionMetric('resetUserPassword', endedAt - startedAt, isError);
        };

        try
        {
            const result = await HousekeepingApi.resetUserPassword(userId);

            if(!result || result.ok === false)
            {
                markActionDone(result?.message || 'housekeeping.action.error', null);
                measure(true);
                return result ?? null;
            }

            const plaintext = result.message ?? '';

            if(plaintext) revealPassword(userId, username, plaintext);

            // Generic success key — does NOT include the plaintext, so
            // even if the banner is visible the password isn't in it.
            markActionDone(null, 'housekeeping.action.reset_password.done');
            measure(false);
            return result;
        }
        catch(error)
        {
            markActionDone(String((error as Error)?.message ?? error), null);
            measure(true);
            return null;
        }
    }, [ markActionPending, markActionDone, selectedUser, revealPassword, recordActionMetric ]);

    const setUserRank = useCallback(async (userId: number, rank: number) =>
    {
        if(!validationOr(validatePositiveId(userId, 'user'), markActionDone)) return null;
        if(!validationOr(validateRank(rank), markActionDone)) return null;

        const result = await runAction(() => HousekeepingApi.setUserRank(userId, rank), 'setUserRank');

        if(result && result.ok !== false && selectedUser && selectedUser.id === userId)
        {
            setSelectedUser({ ...selectedUser, rank });
        }

        return result;
    }, [ runAction, markActionDone, selectedUser, setSelectedUser ]);

    const tradeLockUser = useCallback(async (userId: number, hours: number, reason: string) =>
    {
        if(!validationOr(validatePositiveId(userId, 'user'), markActionDone)) return null;
        if(!validationOr(validateReason(reason), markActionDone)) return null;
        if(!validationOr(validateBanHours(hours), markActionDone)) return null;

        return runAction(() => HousekeepingApi.tradeLockUser(userId, hours, reason), 'tradeLockUser');
    }, [ runAction, markActionDone ]);

    // -- ROOM --------------------------------------------------------
    const openRoom = useCallback(async (roomId: number) =>
    {
        if(!validationOr(validatePositiveId(roomId, 'room'), markActionDone)) return null;

        const result = await runAction(() => HousekeepingApi.openRoom(roomId), 'openRoom');

        if(result && result.ok !== false && selectedRoom && selectedRoom.id === roomId)
        {
            setSelectedRoom({ ...selectedRoom, isLocked: false });
        }

        return result;
    }, [ runAction, markActionDone, selectedRoom, setSelectedRoom ]);

    const closeRoom = useCallback(async (roomId: number) =>
    {
        if(!validationOr(validatePositiveId(roomId, 'room'), markActionDone)) return null;

        const result = await runAction(() => HousekeepingApi.closeRoom(roomId), 'closeRoom');

        if(result && result.ok !== false && selectedRoom && selectedRoom.id === roomId)
        {
            setSelectedRoom({ ...selectedRoom, isLocked: true });
        }

        return result;
    }, [ runAction, markActionDone, selectedRoom, setSelectedRoom ]);

    const muteRoom = useCallback(async (roomId: number, minutes: number) =>
    {
        if(!validationOr(validatePositiveId(roomId, 'room'), markActionDone)) return null;
        if(!validationOr(validateBanHours(minutes), markActionDone)) return null;

        return runAction(() => HousekeepingApi.muteRoom(roomId, minutes), 'muteRoom');
    }, [ runAction, markActionDone ]);

    const kickAllFromRoom = useCallback(async (roomId: number) =>
    {
        if(!validationOr(validatePositiveId(roomId, 'room'), markActionDone)) return null;

        return runAction(() => HousekeepingApi.kickAllFromRoom(roomId), 'kickAllFromRoom');
    }, [ runAction, markActionDone ]);

    const transferRoomOwnership = useCallback(async (roomId: number, newOwnerId: number) =>
    {
        if(!validationOr(validatePositiveId(roomId, 'room'), markActionDone)) return null;
        if(!validationOr(validatePositiveId(newOwnerId, 'user'), markActionDone)) return null;

        return runAction(() => HousekeepingApi.transferRoomOwnership(roomId, newOwnerId), 'transferRoomOwnership');
    }, [ runAction, markActionDone ]);

    const deleteRoom = useCallback(async (roomId: number) =>
    {
        if(!validationOr(validatePositiveId(roomId, 'room'), markActionDone)) return null;

        const result = await runAction(() => HousekeepingApi.deleteRoom(roomId), 'deleteRoom');

        if(result && result.ok !== false && selectedRoom && selectedRoom.id === roomId)
        {
            setSelectedRoom(null);
        }

        return result;
    }, [ runAction, markActionDone, selectedRoom, setSelectedRoom ]);

    // -- ECONOMY -----------------------------------------------------
    const giveCredits = useCallback(async (userId: number, amount: number) =>
    {
        if(!validationOr(validatePositiveId(userId, 'user'), markActionDone)) return null;
        if(!validationOr(validateAmount(amount), markActionDone)) return null;

        return runAction(() => HousekeepingApi.giveCredits(userId, amount), 'giveCredits');
    }, [ runAction, markActionDone ]);

    const giveDuckets = useCallback(async (userId: number, amount: number) =>
    {
        if(!validationOr(validatePositiveId(userId, 'user'), markActionDone)) return null;
        if(!validationOr(validateAmount(amount), markActionDone)) return null;

        return runAction(() => HousekeepingApi.giveDuckets(userId, amount), 'giveDuckets');
    }, [ runAction, markActionDone ]);

    const giveDiamonds = useCallback(async (userId: number, amount: number) =>
    {
        if(!validationOr(validatePositiveId(userId, 'user'), markActionDone)) return null;
        if(!validationOr(validateAmount(amount), markActionDone)) return null;

        return runAction(() => HousekeepingApi.giveDiamonds(userId, amount), 'giveDiamonds');
    }, [ runAction, markActionDone ]);

    const grantItem = useCallback(async (userId: number, itemId: number, quantity: number) =>
    {
        if(!validationOr(validatePositiveId(userId, 'user'), markActionDone)) return null;
        if(!validationOr(validatePositiveId(itemId, 'user'), markActionDone)) return null;
        if(!validationOr(validateAmount(quantity), markActionDone)) return null;

        return runAction(() => HousekeepingApi.grantItem(userId, itemId, quantity), 'grantItem');
    }, [ runAction, markActionDone ]);

    const setHcSubscription = useCallback(async (userId: number, days: number) =>
    {
        if(!validationOr(validatePositiveId(userId, 'user'), markActionDone)) return null;
        if(!validationOr(validateAmount(days), markActionDone)) return null;

        return runAction(() => HousekeepingApi.setHcSubscription(userId, days), 'setHcSubscription');
    }, [ runAction, markActionDone ]);

    const sendHotelAlert = useCallback(async (message: string) =>
    {
        if(!validationOr(validateReason(message), markActionDone)) return null;

        return runAction(() => HousekeepingApi.sendHotelAlert(message), 'sendHotelAlert');
    }, [ runAction, markActionDone ]);

    // -- LIVE IN-ROOM ACTIONS ---------------------------------------
    // These bridge directly to the active RoomSession so the
    // sanction lands on the current game state (no server roundtrip
    // through the HTTP layer). Use for "the user is here, right
    // now" sanctions; persistent admin actions still go through the
    // HTTP API above.
    const kickFromCurrentRoom = useCallback((webUserId: number) =>
    {
        const session = GetRoomSession();

        if(!session)
        {
            markActionDone('housekeeping.live.no_room', null);

            return false;
        }

        try
        {
            session.sendKickMessage(webUserId);
            markActionDone(null, 'housekeeping.live.kicked');
            showSingleBubble(localizeOrPassthrough('housekeeping.live.kicked'), NotificationBubbleType.INFO);

            return true;
        }
        catch(error)
        {
            markActionDone(String((error as Error)?.message ?? error), null);

            return false;
        }
    }, [ markActionDone, showSingleBubble ]);

    const banFromCurrentRoom = useCallback((webUserId: number, severity: 'hour' | 'day' | 'perm' = 'hour') =>
    {
        const session = GetRoomSession();

        if(!session)
        {
            markActionDone('housekeeping.live.no_room', null);

            return false;
        }

        const code = severity === 'perm' ? 'RWUAM_BAN_USER_PERM' : severity === 'day' ? 'RWUAM_BAN_USER_DAY' : 'RWUAM_BAN_USER_HOUR';

        try
        {
            session.sendBanMessage(webUserId, code);
            markActionDone(null, 'housekeeping.live.banned');
            showSingleBubble(localizeOrPassthrough('housekeeping.live.banned'), NotificationBubbleType.INFO);

            return true;
        }
        catch(error)
        {
            markActionDone(String((error as Error)?.message ?? error), null);

            return false;
        }
    }, [ markActionDone, showSingleBubble ]);

    // -- BULK HTTP ACTIONS ------------------------------------------
    // Loop with Promise.allSettled so a single failure doesn't abort
    // the rest of the batch. Aggregated success/failure counts land
    // in the status banner; per-user errors fall through to the audit
    // log on the server side.
    const runBulk = useCallback(async (
        userIds: ReadonlyArray<number>,
        single: (id: number) => Promise<IHousekeepingActionResult | null>,
        actionLabel: string
    ): Promise<{ ok: number; failed: number }> =>
    {
        if(userIds.length === 0) return { ok: 0, failed: 0 };

        markActionPending();

        const startedAt = typeof performance !== 'undefined' ? performance.now() : Date.now();
        const settled = await Promise.allSettled(userIds.map(id => single(id)));
        let ok = 0;
        let failed = 0;

        for(const outcome of settled)
        {
            if(outcome.status === 'fulfilled' && outcome.value && outcome.value.ok !== false) ok++;
            else failed++;
        }

        // One metric sample per bulk run rather than per user — the
        // bulk timing is what the operator cares about. Bucket suffix
        // `:bulk` keeps the metric separate from the matching single
        // action in the telemetry panel.
        const endedAt = typeof performance !== 'undefined' ? performance.now() : Date.now();

        recordActionMetric(`${ actionLabel }:bulk`, endedAt - startedAt, failed > 0);

        const summaryKey = failed === 0 ? 'housekeeping.bulk.success' : 'housekeeping.bulk.partial';

        markActionDone(failed > 0 && ok === 0 ? 'housekeeping.bulk.failed' : null, failed === 0 ? summaryKey : null);
        showSingleBubble(`${ localizeOrPassthrough('housekeeping.bulk.done') } — ${ ok }/${ userIds.length }`, NotificationBubbleType.INFO);

        return { ok, failed };
    }, [ markActionPending, markActionDone, showSingleBubble, recordActionMetric ]);

    const banUsersBulk = useCallback((userIds: ReadonlyArray<number>, reason: string, hours: number) =>
        runBulk(userIds, id => HousekeepingApi.banUser(id, reason, hours), 'banUser'),
    [ runBulk ]);

    const kickUsersBulk = useCallback((userIds: ReadonlyArray<number>, reason: string) =>
        runBulk(userIds, id => HousekeepingApi.kickUser(id, reason), 'kickUser'),
    [ runBulk ]);

    const muteUsersBulk = useCallback((userIds: ReadonlyArray<number>, reason: string, minutes: number) =>
        runBulk(userIds, id => HousekeepingApi.muteUser(id, reason, minutes), 'muteUser'),
    [ runBulk ]);

    const muteInCurrentRoom = useCallback((webUserId: number, minutes: number) =>
    {
        const session = GetRoomSession();

        if(!session)
        {
            markActionDone('housekeeping.live.no_room', null);

            return false;
        }

        try
        {
            session.sendMuteMessage(webUserId, minutes);
            markActionDone(null, 'housekeeping.live.muted');
            showSingleBubble(localizeOrPassthrough('housekeeping.live.muted'), NotificationBubbleType.INFO);

            return true;
        }
        catch(error)
        {
            markActionDone(String((error as Error)?.message ?? error), null);

            return false;
        }
    }, [ markActionDone, showSingleBubble ]);

    return {
        banUser,
        unbanUser,
        muteUser,
        kickUser,
        forceDisconnectUser,
        resetUserPassword,
        setUserRank,
        tradeLockUser,
        openRoom,
        closeRoom,
        muteRoom,
        kickAllFromRoom,
        transferRoomOwnership,
        deleteRoom,
        giveCredits,
        giveDuckets,
        giveDiamonds,
        grantItem,
        setHcSubscription,
        sendHotelAlert,
        kickFromCurrentRoom,
        banFromCurrentRoom,
        muteInCurrentRoom,
        banUsersBulk,
        kickUsersBulk,
        muteUsersBulk
    };
};
