import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { StyleSheet, Text, TextInput, useColorScheme, View } from 'react-native';
import { Picker } from 'react-native-web';
import { Button } from '../components/index.js';
import { DEFAULT_ACCENT_COLOUR, HIGHLIGHT_COLOUR_DARK, HIGHLIGHT_COLOUR_LIGHT, TEXT_COLOUR_DARK, TEXT_COLOUR_LIGHT } from '../constants.js';
import ipc, { events } from '../ipc.js';
import { getAccounts, RequestState, Root, useAsync, useDiscordPresenceSource, useEventListener } from '../util.js';

export interface DiscordSetupProps {
    users?: string[];
    friend_nsa_id?: string;
    /** @default true */
    show_preferences_button?: boolean;
}

enum DiscordSourceType {
    CORAL,
    URL,
    NONE,
}

export default function DiscordSetup(props: DiscordSetupProps) {
    const colour_scheme = useColorScheme();
    const theme = colour_scheme === 'light' ? light : dark;

    const [accent_colour, setAccentColour] = React.useState(() => ipc.getAccentColour());
    useEventListener(events, 'systemPreferences:accent-colour', setAccentColour, []);

    const [users, ,, forceRefreshAccounts] = useAsync(useCallback(() => getAccounts(), [ipc]));
    useEventListener(events, 'update-nintendo-accounts', forceRefreshAccounts, []);

    const [discord_presence_source, discord_presence_source_state] = useDiscordPresenceSource();

    const [selectedMode, setSelectedMode] = useState(props.users?.length && props.friend_nsa_id ?
        DiscordSourceType.CORAL : DiscordSourceType.NONE);
    const [selectedUserId, setSelectedUserId] = useState<string | null>(null);
    const [selectedFriendNsaId, setSelectedFriendNsaId] = useState<string | null>(props.friend_nsa_id ?? null);
    const [presenceUrl, setPresenceUrl] = useState('');

    useEffect(() => {
        if (!discord_presence_source) {
            setSelectedMode(props.friend_nsa_id ? DiscordSourceType.CORAL : DiscordSourceType.NONE);
        } else if ('na_id' in discord_presence_source) {
            setSelectedMode(DiscordSourceType.CORAL);
            setSelectedUserId(discord_presence_source.na_id);
            setSelectedFriendNsaId(discord_presence_source.friend_nsa_id ?? null);
        } else if ('url' in discord_presence_source) {
            setSelectedMode(DiscordSourceType.URL);
            setPresenceUrl(discord_presence_source.url);
        }
    }, [discord_presence_source]);

    const user = useMemo(() => selectedUserId ? users?.find(u => u.user.id === selectedUserId) : undefined,
        [selectedUserId, users]);
    const [friends, , friends_state, forceRefreshFriends] = useAsync(useCallback(() => user?.nsotoken ?
        ipc.getNsoFriends(user.nsotoken) : Promise.resolve(null), [ipc, user?.nsotoken]));
    const friends_with_presence = useMemo(() => friends?.filter(f => f.presence.updatedAt ||
        f.nsaId === selectedFriendNsaId), [friends, selectedFriendNsaId]);
    const friend = useMemo(() => selectedFriendNsaId ? friends?.find(f => f.nsaId === selectedFriendNsaId) : undefined,
        [selectedFriendNsaId, friends]);

    const filtered_users = useMemo(() => users?.filter(u => selectedUserId === u.user.id ||
        (u.nso && (!props.users || props.users.includes(u.user.id)))), [users, props.users, selectedUserId]);

    useEventListener(events, 'window:refresh', () => (forceRefreshAccounts(), forceRefreshFriends()), []);

    useEffect(() => {
        if (filtered_users?.length && !user) setSelectedUserId(filtered_users[0].user.id);
    }, [filtered_users, user]);

    useEffect(() => {
        if (friends?.length && !friend) setSelectedFriendNsaId(friends[0].nsaId);
    }, [friends, user]);

    const save = useCallback(async () => {
        if (selectedMode === DiscordSourceType.CORAL) {
            if (!selectedUserId?.match(/^[0-9a-f]{16}$/)) throw new Error('Invalid Nintendo Account ID');
            if (!selectedFriendNsaId?.match(/^[0-9a-f]{16}$/)) throw new Error('Invalid friend Network Service Account ID');

            await ipc.setDiscordPresenceSource({na_id: selectedUserId, friend_nsa_id: selectedFriendNsaId});
        } else if (selectedMode === DiscordSourceType.URL) {
            await ipc.setDiscordPresenceSource({url: presenceUrl});
        } else {
            await ipc.setDiscordPresenceSource(null);
        }

        window.close();
    }, [selectedMode, selectedUserId, selectedFriendNsaId, presenceUrl]);

    useEffect(() => {
        const handler = (event: KeyboardEvent) => event.key === 'Escape' && window.close();
        window.addEventListener('keydown', handler);
        return () => window.removeEventListener('keydown', handler);
    }, []);

    if (!users || discord_presence_source_state !== RequestState.LOADED) {
        return null;
    }

    return <Root title="Discord Rich Presence setup" scrollable autoresize>
        <View style={styles.main}>
            <Text style={theme.text}>Discord Rich Presence setup</Text>

            <Text style={[styles.header, theme.text]}>1. Select mode</Text>

            <Picker<DiscordSourceType> selectedValue={selectedMode} onValueChange={v => setSelectedMode(parseInt(v))}
                style={[styles.picker, theme.picker]}
            >
                <Picker.Item label="Select a user that is friends with the user you want to share"
                    value={DiscordSourceType.CORAL} />
                <Picker.Item label="Enter a URL that returns the presence data you want to share"
                    value={DiscordSourceType.URL} />
                <Picker.Item label="Disable" value={DiscordSourceType.NONE} />
            </Picker>

            {selectedMode === DiscordSourceType.CORAL ? <>
                <Text style={[styles.header, theme.text]}>2. Select user</Text>
                <Text style={[styles.help, theme.text]}>This user must be friends with the user you want to share.</Text>

                <Picker<string> selectedValue={selectedUserId ?? ''} onValueChange={setSelectedUserId}
                    style={[styles.picker, theme.picker]}
                >
                    {filtered_users?.map(u => <Picker.Item
                        key={u.user.id}
                        label={u.user.nickname +
                            (u.user.nickname !== u.nso!.nsoAccount.user.name ? '/' + u.nso!.nsoAccount.user.name : '')}
                        value={u.user.id}
                    />)}
                </Picker>

                {props.friend_nsa_id && (!selectedFriendNsaId || selectedFriendNsaId === props.friend_nsa_id) ? <>
                    <Text style={[styles.header, theme.text]}>3. Select friend</Text>
                    <Text style={[styles.help, theme.text]}>This is the user you want to share.</Text>

                    <Picker<string> selectedValue={selectedFriendNsaId ?? ''} onValueChange={setSelectedFriendNsaId}
                        style={[styles.picker, theme.picker]}
                        enabled={false}
                    >
                        <Picker.Item key={props.friend_nsa_id} label={friend?.name ?? props.friend_nsa_id}
                            value={props.friend_nsa_id} />
                    </Picker>
                </> : user && friends_with_presence ? <>
                    <Text style={[styles.header, theme.text]}>3. Select friend</Text>
                    <Text style={[styles.help, theme.text]}>This is the user you want to share.</Text>

                    <Picker<string> selectedValue={selectedFriendNsaId ?? ''} onValueChange={setSelectedFriendNsaId}
                        style={[styles.picker, theme.picker]}
                    >
                        {friends_with_presence.map(f => <Picker.Item key={f.nsaId} label={f.name} value={f.nsaId} />)}
                    </Picker>
                </> : null}
            </> : null}

            {selectedMode === DiscordSourceType.URL ? <>
                <Text style={[styles.header, theme.text]}>2. Enter presence URL</Text>
                <Text style={[styles.help, theme.text]}>This must be a HTTPS URL that returns a JSON object with either a user, friend or presence key. This is intended to be used with nxapi's znc API proxy.</Text>

                <TextInput value={presenceUrl} onChangeText={setPresenceUrl}
                    placeholder="https://nxapi.example.com/api/znc/friend/..."
                    style={[styles.textInput, theme.textInput]} />
            </> : null}

            {discord_presence_source && (props.show_preferences_button ?? true) ? <>
                <Text style={[styles.help, styles.header, theme.text]}>Configure additional options for Discord Rich Presence</Text>
                <View style={[styles.button, styles.buttonPreferences]}>
                    <Button title="Preferences"
                        onPress={() => ipc.showPreferencesWindow()}
                        color={'#' + (accent_colour ?? DEFAULT_ACCENT_COLOUR)} />
                </View>
            </> : null}

            <View style={styles.buttons}>
                <View style={styles.button}>
                    <Button title="Cancel"
                        onPress={() => window.close()}
                        color={'#' + (accent_colour ?? DEFAULT_ACCENT_COLOUR)} />
                </View>
                <View style={styles.button}>
                    <Button title="Save"
                        onPress={save}
                        primary
                        color={'#' + (accent_colour ?? DEFAULT_ACCENT_COLOUR)} />
                </View>
            </View>
        </View>
    </Root>;
}

const styles = StyleSheet.create({
    loading: {
        flex: 1,
        paddingVertical: 50,
        paddingHorizontal: 20,
        justifyContent: 'center',
    },

    main: {
        flex: 1,
        paddingVertical: 20,
        paddingHorizontal: 20,
    },

    header: {
        marginTop: 12,
    },
    help: {
        marginTop: 8,
        fontSize: 13,
        opacity: 0.7,
    },

    picker: {
        marginTop: 8,
        paddingVertical: 4,
        paddingHorizontal: 8,
        borderWidth: 0,
        borderRadius: 3,
        fontSize: 13,
    },
    textInput: {
        marginTop: 8,
        paddingVertical: 5,
        paddingHorizontal: 10,
        borderRadius: 3,
        fontSize: 13,
    },

    buttons: {
        marginTop: 20,
        flexDirection: 'row',
        justifyContent: 'flex-end',
    },
    button: {
        marginLeft: 10,
    },
    buttonPreferences: {
        marginTop: 10,
        marginLeft: 0,
        flexDirection: 'row',
        justifyContent: 'flex-start',
    },
});

const light = StyleSheet.create({
    text: {
        color: TEXT_COLOUR_LIGHT,
    },
    picker: {
        backgroundColor: HIGHLIGHT_COLOUR_LIGHT,
        color: TEXT_COLOUR_LIGHT,
    },
    textInput: {
        backgroundColor: HIGHLIGHT_COLOUR_LIGHT,
        color: TEXT_COLOUR_LIGHT,
    },
});

const dark = StyleSheet.create({
    text: {
        color: TEXT_COLOUR_DARK,
    },
    picker: {
        backgroundColor: HIGHLIGHT_COLOUR_DARK,
        color: TEXT_COLOUR_DARK,
    },
    textInput: {
        backgroundColor: HIGHLIGHT_COLOUR_DARK,
        color: TEXT_COLOUR_DARK,
    },
});
